顺利集成 PayPal






4.64/5 (6投票s)
如何轻松地从 Windows C++ 应用程序中集成和接口 PayPal Express Checkout API
引言
本文是在需要将 PayPal Express Checkout 集成到 c++ Win32 应用程序中后编写的。
背景
我一直在考虑在后台集成,除了付款页面外,没有任何其他网站,作为 c++ 中的桌面应用程序的一部分。 是否可以遵循以下场景
- 生成发票/销售单,并通过 REST API 获取即将进行的交易的某种唯一 ID。
- 使用唯一 ID 重定向到 Paypal 网站的临时支付页面。
- 在后台,每隔几分钟通过 REST API 检查是否已付款。
解决方案
我找到了一种方法,并为内置支付处理引擎创建了一个 POC,该引擎允许您接受来自任何信用卡持卡人的付款(无论是否为 PayPal 客户),并支付解锁软件产品或特定功能的费用。
要处理付款,您需要申请成为 PayPal 开发者 并获得您自己的 PayPal 凭据。 然后您将收到 2 组凭据。 一个用于测试(“沙盒”),另一个用于真实环境。
首先,您应该测试沙盒以测试 API。
我创建了一个 PayPal 类,其中一个“init()”用于“沙盒”和真实环境交易。
Void InitPayPal(BOOL Sandbox, LPTSTR User, LPTSTR password, LPTSTR signature, LPTSTR successUrl, LPTSTR failedURL)
沙盒 – 指示您是使用 PayPal 的沙盒帐户测试集成,还是上线。
用户 – 您的 PayPal 用户名
密码 – 您的 PayPal 密码
签名 – 您的 PayPal 签名
successUrl – 指向您希望在成功付款后显示的网页的 URL。
failedURL – 指向您希望在付款失败/取消后显示的网页的 URL。
InitPayPal() 函数非常简单
void InitPayPal(BOOL Sandbox, LPTSTR User, LPTSTR password, LPTSTR signature, LPTSTR successUrl, LPTSTR failedURL, LPWSTR ProductName) { m_sandbox = Sandbox; m_user = User; m_password = password; m_signature = signature; m_SuccessURL = successUrl; m_FailureURL = failedURL; m_ProductName = ProductName; CUR_CHAR = L"$"; SYSTEMTIME st; GetSystemTime(&st); g_tPayStart = CTime(st); InitilizedPaypal = TRUE; }
启动付款
当您希望从您的程序启动付款时,您可以调用我编写的以下函数,该函数通常构建一个字符串 (ExpChkoutStr) 并使用以下 PayPal API 调用
对于所有 HTTP 通信,我使用了由 Cheng Shi 开发的 WinHTTP 类。
以下是我如何向 PayPal 服务器发送初始请求
// Send string to PayPal server WinHttpClient WinClient1(ExpChkoutStr.GetBuffer()); WinClient1.SetRequireValidSslCertificates(false); // Now we get PayPal's response: WinClient1.SendHttpRequest(L"GET"); httpResponseContent1 = WinClient1.GetResponseContent(); CString strTransactionRet = UrlDecode(httpResponseContent1.c_str());
如您所见,我们正在向 PayPal 发送一个长字符串,我们使用另一个函数生成该字符串。 此函数将凭据、请求交易的性质、其他详细信息全部组合成一个字符串。
CString result; result = (m_sandbox) ? PAYPAL_SANDBOX_HTTPS : PAYPAL_REAL_HTTPS; result += Q_USER; result += m_user; result += AND_PASSWORD; result += m_password; result += AND_SIGNATURE; result += m_signature; result += AND_PAYMENTAMOUNT; result += strAmount; result += L"&METHOD=SetExpressCheckout"; result += AND_RETURN_URL; result += m_SuccessURL; result += AND_CANCEL_URL; result += m_FailureURL; result += AND_VERSION; result += L"&NOSHIPPING=1"; result += L"&ADDROVERRIDE=0&BRANDNAME=Secured Globe, Inc."; result += L"&PAYMENTREQUEST_0_DESC="; result += L"Item name: " + strUnits + L"(" + UnitName + L") "; result += L"Price: " + strAmount; result += L"&NOTETOBUYER=Here you can add a note to the buyer";
现在, result 将保存要在先前代码块中发送的字符串。
然后,我们检查从 PayPal 收到的结果
PayPal 服务器返回的结果是一个“令牌”,用于确定必须打开的一次性网页(LinkToOpen),以便最终用户确认购买
// Extract token from response CString sToken = ExtractElement(strTransactionRet, L"TOKEN"); if (sToken == L"") { wprintf(L"Internal error: (Paypal): no token was generated (%s)", strTransactionRet); MessageBox(NULL, L"Internal payment processing error", L"", MB_OK); return FALSE; } CString LinkToOpen = (m_sandbox) ? SANDBOX_PAYPAL_CHECKOUT : REAL_PAYPAL_CHECKOUT; LinkToOpen += L"&token="; LinkToOpen += sToken;
为了从 PayPal 服务器响应中提取元素,我们编写了这个小函数
CString ExtractElement(CString EntireString, CString ElementName) { CString result = L""; CString WhatToFind = ElementName + L"="; int foundToken = EntireString.Find(WhatToFind); if (foundToken > -1) { int EndToken = EntireString.Find(L"&", foundToken); if (EndToken != -1) { result = EntireString.Mid(foundToken + ElementName.GetLength()+1, EndToken - foundToken - ElementName.GetLength()-1); } } return result; }
调用一次性网页
下一步是使用由 PayPal 服务器托管的一次性生成的安全网页打开默认 Web 浏览器
STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); CString command_line; command_line.Format(L"cmd.exe /c start \"link\" \"%s\" ", LinkToOpen); // LinkToOpen if (!CreateProcess(NULL, // No module name (use command line) command_line.GetBuffer(), NULL, // Process handle not inheritable NULL, // Thread handle not inhberitable FALSE, // Set handle inheritance to FALSE NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure &pi) // Pointer to PROCESS_INFORMATION structure ) { wprintf(L"CreateProcess failed (%d).\n", GetLastError()); // At this stage you would want to mark this transaction as "failed" return FALSE; }
然后,其余的是维护一个包含所有待处理交易的小数据库,并跟踪每个交易,直到它成功、失败、取消或超时。
用户界面
我们已在小型 DRM 组件中集成了一个最小的 UX,如下所示
然后,一次性网页将如下所示
示例来自真实产品 Datattoo Recovery。