paypal支付接口的对接

paypal 开发人员需要先到 paypal 开发者网站注册一个用于测试的帐号,注册的网址为:https://developer.paypal.com
好了之后再进入 Sandbox 建立测试用的 Paypal 虚拟帐号(至少应该建立一个 Business 的和一个 Personal 的),这个虚拟帐号可以建一个商家号和一个支付号,可用于测试,里面的金额可以自由设定。

商家号那里一般填写的是邮箱(测试的时候,正式的商家号不是邮箱)。
需要注意的是:

1、把相关配置信息尽量放到数据库中。
2、测试帐号下的支付完成后是不会触发异步回调地址的,需要到开发者平台的去模拟相关触发。
3、为了回调支付准确,增加了一个可以自定义的验证密钥,参与加密验证。

下面是具体实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<?php
namespace Home\Controller;

use Think\Controller;
use Common\Controller\HomeController;
class PayController extends HomeController {
/**
* 基本配置信息
*/
function __construct() {
parent::__construct();
$testConfig = array(
'payUrl' => 'https://www.sandbox.paypal.com/cgi-bin/webscr', //支付地址
'business' => '3HSV6******', //商家号
'currency_code' => 'USD', //支付币种 具体代码可参考:https://developer.paypal.com/docs/classic/api/currency_codes/
'lc' => 'US', //支付页面的语言 具体代码可参考:https://developer.paypal.com/docs/classic/api/country_codes/
'key' => 'webape', //个人加密字符串
);
$prodConfig = array(
'payUrl' => 'https://www.paypal.com/cgi-bin/webscr', //支付地址
'business' => '3HSV6******', //商家号
'currency_code' => 'USD', //支付币种 具体代码可参考:https://developer.paypal.com/docs/classic/api/currency_codes/
'lc' => 'US', //支付页面的语言 具体代码可参考:https://developer.paypal.com/docs/classic/api/country_codes/
'key' => 'webape', //个人加密字符串
);
$payConfig = $testConfig; //这里切换测试环境和生产环境
$this->payConfig = $payConfig;
}

/**
* paypal支付提交处理
*/
public function paypalpay() {
$oid = I('oid');
//数据信息
$order = M('order')->find($oid); //订单信息
if (!$order) {
$this->display('Public:404');
exit();
}
$data = $this->payConfig;
//订单详情信息
$orderDetail = M('order_detail')->where(array('oid' => $oid))->select();
$goodsInfo = '';
foreach ($orderDetail as $k => $v) {
$product = M($v['name'])->field('title')->find($v['pid']);
$product_color = M($v['name'] . '_colors')->field('title')->find($v['color_id']);
$goodsInfo .= $product['title'] . '-' . $product_color['title'] . '-' . $v['diameter'] . '-' . $v['number'] . '-';
}
//加密传值
$signSrc = '';
$signArr = array(
'invoice' => $order['order_num'], //自定义订单号
'key' => $data['key'], //自定义的加密字符串
);
foreach ($signArr as $k => $v) {
$signSrc .= $v;
}
$signInfo = strtoupper(hash("sha256", $signSrc));
//表单提交
$formData = array(
'cmd' => '_xclick',
'business' => $data['business'],
'item_name' => string_replace($goodsInfo),
'currency_code' => $data['currency_code'],
'amount' => $order['real_money'],
'notify_url' => C('PROTOCOL') . $_SERVER['SERVER_NAME'] . U('Pay/paypalnotifyurl'),
'return' => C('PROTOCOL') . $_SERVER['SERVER_NAME'] . U('Order/info', array('order_num' => $order['order_num'], 'paypal' => 1)), //支付成功后网页跳转地址把order_num带上
'cancel_return' => C('PROTOCOL') . $_SERVER['SERVER_NAME'] . U('Order/info', array('order_num' => $order['order_num'])), //用户取消支付后跳转的地址
'invoice' => $order['order_num'], //自定义订单号
'no_shipping' => 1,
'custom' => $signInfo, //自定义变量 原样返回 订单id号
'charset' => 'utf8',
'lc' => $data['lc'],
);
header("Content-type: text/html; charset=utf-8");
//输出提交表单
$payForm = '<form action="' . $data['payUrl'] . '" method="post" id="form" name="form">' . inputs($formData) . '</form><script>document.form.submit()</script>';
echo $payForm;
}

/**
* paypal回调地址
*/
public function paypalnotifyurl() {
$data = $this->payConfig;
//加密接收值
$getData = array(
'invoice' => I('post.invoice'),
'key' => $data['key'],
);
//键名和键值和上面的原加密字符串一致
$payment_status = I('post.payment_status');
$custom = I('post.custom'); //接收的加密字符串
$signSrc = '';
foreach ($getData as $k => $v) {
$signSrc .= $v;
}
$signInfo = strtoupper(hash("sha256", $signSrc));
//验证与修改
if ($signInfo == $custom && $payment_status == 'Completed') {
//改变订单的状态为已支付
$saveData = array(
'pay_create_time' => time(),
'pay_sign_info' => $signInfo,
'status' => 2,
);
$re = M('order')->where(array('order_num' => $getData['invoice']))->data($saveData)->save();
$payResult = "Congratulations,payment is successful !";
} else {
$payResult = "Data validation failed";
}

/*支付的异步记录备查*/
$logData = I('post.');
$logfile = __ROOT__ . '/payPalLog_c451f8e6e53014.txt';
import("Org.Util.File");
$logObj = new \File($logfile);
$logfile = $logObj->getRealFile();
file_put_contents($logfile, serialize($logData), FILE_APPEND);
}
}
上面有用到一个input组装函数:

/**
* 组装表单域
* @param $data
* @return string
*/
function inputs($data){
$inputs='';
foreach($data as $k=>$v){
$inputs.='<input type="hidden" name="'.$k.'" value="'.$v.'"/>';
}
return $inputs;
}