上次在 PHP 开发详解:PayPal Payment Data Transfer (PDT) 一文中介绍了网站集成 Paypal 付款功能并如何将付款数据返回,能够使得用户在付款完成后继续回到网站上来,并将付款信息告知用户。但是 PayPal Payment Data Transfer 这样的数据返回方式是不保险的,特别是对于用户完成付款后还需要进行后续业务处理,如标注订单为已付款以及减去商品库存等。用户有可能在付款完成后还没跳转到自己网站就已经关闭了页面,而我们的交易数据和业务处理都必须通过访问付款完成后的 ReturnURL 来得到展示与处理。为了获得更加保险的业务处理,我们在集成使用 Paypal 付款功能时需要按需使用 PayPal Instant Payment Notification (IPN)。
PayPal 的开发文档指出:
Instant Payment Notification (IPN) is a message service that notifies you of events related to PayPal transactions. You can use IPN messages to automate back-office and administrative functions, such as fulfilling orders, tracking customers, and providing status and other transaction-related information.
同样,在我根据网上部分代码和 PayPal 开发文档开发使用此功能时,遇到了诸多麻烦,不过经过一些时间的探索,也最终实现了 PayPal Instant Payment Notification (IPN) 的集成使用,我也将自己的探究过程和实现方法记录下来,方便读者。如果你在测试或开发中与任何问题或想法欢迎留言交流。
一、PayPal Instant Payment Notification 的工作流程和原理
IPN 是 PayPal 用来通知你相关交易数据的消息服务,它要求你先在自己的 PayPal 商家账户中设置一个即时付款通知 URL 用来接收通知数据,并且编写相应的代码实现特定的数据接收与验证以及可能的业务处理。我们把这个页面或者这些代码称为 listener。
- 顾客在接入了 PayPal 的应用上通过某种方式成功付款。
- PayPal 通过 HTTP POST 的方式向你的 listener 发送这次交易的数据和通知。
- listener 接收数据并返回一个空的 HTTP 200 响应。
- listener 将接收到的通知数据完整的通过 HTTP POST 的方式返回给 PayPal 用于验证。
- PayPal 根据接收到的信息进行验证,验证通过返回 VERIFIED,否则返回 INVALID。
完整的工作流程如下图:
二、申请 PayPal 开发者账号
这在前面文章介绍 PayPal Payment Data Transfer (PDT) 已经有了详细的说明,在此不再赘述。
三、为商家账号启用 IPN 功能
为了使用 PayPal 的 PayPal Instant Payment Notification,必须启用商家账户的实即时付款通知,并设置好的通告 URL。
- 登录 PayPal 账户后,鼠标滑动到【用户信息】处点击【更多选项】,然后找到【即时付款通知习惯设定】,进入设置页面。
- 点击【选择 IPN 设置】,然后填写好自己的通告 URL,并将【即时付款通知消息】切换为【接收即时付款通知消息(已启用)】,然后点击保存。
这样,我们就设置好了自己的 listener。
四、建立一个 “立即付款” 按钮并集成到自己的应用
这也在前面文章介绍 PayPal Payment Data Transfer (PDT) 已经有了详细的说明,同样不再赘述。
五、获取 IPN 发送过来的交易数据
在我们的 listener 页面,该做的第一件事便是获取 PayPal 发送过来的数据,PayPal 使用 POST 的方式发送数据,我们只需要简单一句代码获取 POST 数据即可:
$ipn_post_data = $_POST;
在获取到数据的同时,实际上 listener 已经自动给 PayPal 返回了 HTTP 200 的状态码。
六、向 PayPal 验证交易数据
既然我们已经获取到了 PayPal 使用 POST 的方式发送过来的数据,接下来则必须要原样返回这些数据给 PayPal,用于验证数据的来源和真实性。我们在这里依然使用 cURL 函数进行提交请求数据:
// Choose url
if(array_key_exists(‘test_ipn’, $ipn_post_data) && 1 === (int) $ipn_post_data[‘test_ipn’])
$url = ‘https://www.sandbox.paypal.com/cgi-bin/webscr’;
else
$url = ‘https://www.paypal.com/cgi-bin/webscr’;
// Set up request to PayPal
$request = curl_init();
curl_setopt_array($request, array
(
CURLOPT_URL => $url,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => http_build_query(array(‘cmd’ => ‘_notify-validate’) + $ipn_post_data),
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HEADER => FALSE,
CURLOPT_SSL_VERIFYPEER => TRUE,
CURLOPT_CAINFO => ‘cacert.pem’,
));
// Execute request and get response and status code
$response = curl_exec($request);
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
// Close connection
curl_close($request);
if($status == 200 && $response == ‘VERIFIED’)
{
// All good! Proceed…
}
else
{
// Not good. Ignore, or log for investigation…
}
在这段代码的最开始设置了对 test_ipn 字段的检测,用于区分测试环境和真实的生产环境,能够方便的应用于测试或者真实生产环境。此外,代码中的 CURLOPT_CAINFO => ‘cacert.pem’,这个证书文件可以前往 http://curl.haxx.se/docs/caextract.html 进行下载。
七、调整编码
经过第六步后,如果我们得到的验证结果是 VERIFIED,接下来就可以使用这些交易信息了。但是有时候我们获得的数据的编码并不是我们所想要的,这时候可能就需要调整数据编码为我们所需要的。一般来说,我们可以将编码调整为 UTF-8。
if(array_key_exists(‘charset’, $ipn_data) && ($charset = $ipn_data[‘charset’]))
{
// Ignore if same as our default
if($charset == ‘utf-8’)
return;
// Otherwise convert all the values
foreach($ipn_data as $key => &$value)
{
$value = mb_convert_encoding($value, ‘utf-8’, $charset);
}
// And store the charset values for future reference
$ipn_data[‘charset’] = ‘utf-8’;
$ipn_data[‘charset_original’] = $charset;
}
关于编码,还有一个需要注意的地方,有时候在调试的时候,无论怎么样都无法使交易信息成功通过 IPN 通知到 listener,这个时候可能要考虑 PayPal 的语言编码设置。例如你的网站代码都是 UTF-8 编码,则也应该在【用户信息】【更多选项】【语言编码】的设置页面中点击【更多选项】,将编码调整为 UTF-8。
八、在正式生产环境中使用 PayPal Instant Payment Notification
经过前面的步骤后,我们已经将使用 PayPal Instant Payment Notification 的流程和部分关键代码弄清楚,在开发测试中,首先在 sandbox 中测试通过,然后就可以应用到真实的生产环境了。在真实的生产环境中使用 PayPal Instant Payment Notification 和我们前文所述方法步骤完全一致。
九、示例代码
前文已经详细介绍了 PayPal Instant Payment Notification 的 PHP 开发步骤,中间夹带了部分重要代码片段,由于前面的代码比较分散,为方便大家参考,这里也提供一份示例代码,你可以 访问这里 来获取代码。
至此,PayPal Instant Payment Notification 的 PHP 开发详解已经介绍完毕,希望大家看过有所收获。如果你在阅读过程中发现错误或者对文章有任何观点欢迎留言讨论。最后,此篇文章的形成离不开我在探索过程中所参阅的各种网络资料,对它们的作者表示感谢!