微信支付文档
前期准备流程
- 微信公众号申请
- 支付商户号申请
- 微信公众号绑定支付商户号
4.设置白名单、生成AppSecret(开发-基本配置)
5.申请Api证书、设置APIv3密钥
6.设置支付配置、jsapi支付授权目录、Native支付回调链接(产品中心-开发配置)
支付流程
1.前端调用后端下单接口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/**
* 创建订单
* @param {*} active
* @param {*} mobile
* @param {*} token
*/
function createOrder(active, mobile, token) {
$.showLoading();
$.ajax({
type: 'post',
url: url,
data: { 'active': active, 'pay_type': 'wechat', 'mobile': mobile },
dataType: 'json',
beforeSend: function (XMLHttpRequest) {
XMLHttpRequest.setRequestHeader("token", token);
},
success: function (res) {
try {
if (res.status == 'error' || res.code != 200) {
$.toptip(res.message, 'warning')
return false;
}
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId": res.data.appId,
"timeStamp": res.data.timeStamp,
"nonceStr": res.data.nonceStr,
"package": res.data.package,
"signType": res.data.signType,
"paySign": res.data.paySign
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
$.toptip('支付成功', 'success')
}
});
} catch (err) {
toLogin();
}
},
error: function (res) {
$.toptip('网络错误', 'warning')
}, complete: function (res) {
$.hideLoading();
}
})
}
<a href="javascript:;" class="weui-btn weui-btn_primary" onclick="createOrder(1,mobile,token)">立即支付</a>
2.处理信息、生成订单、返回支付信息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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163/**
* 生成订单
* @param $userId
* @param $mobile
* @param int $active
* @param string $payType
* @return array|mixed
* @throws ApiException
* @throws \Throwable
*/
public function createOrder($userId, $mobile, $active = 1, $payType = 'wechat')
{
DB::beginTransaction();
try {
if ($this->countWaitOrderByUserId($userId)) {
throw new ApiException('您有未支付的订单,请先处理完毕', 500);
}
$activity = $this->findActive($active);
if (!$activity) {
throw new ApiException('充值金额有误', 500);
}
$orderId = StringHelper::getOrderId('R');
$orderData = [
'order_id' => $orderId,
'user_id' => $userId,
'original_price' => $activity->original_price,
'price' => $activity->price,
'pay_type' => $payType,
'mobile' => $mobile,
'active' => $active
];
if (!$this->insertOrder($orderData)) {
throw new ApiException('订单创建失败', 500);
}
// 请求微信统一下单
if ($result = $this->payOrder($orderId, $userId)) {
DB::commit();
return $result;
}
throw new ApiException('统一下单失败', 500);
} catch (\Exception $exception) {
DB::rollBack();
throw new ApiException($exception->getMessage(), 500);
}
}
/**
* 微信统一下单
* @param $orderId
* @param $userId
* @return mixed
* @throws ApiException
* @throws GuzzleException
*/
public function payOrder($orderId, $userId)
{
$orderInfo = $this->findOrderByOrderId($orderId);
if (!$orderInfo || $orderInfo->user_id != $userId) {
throw new ApiException('订单不存在', 500);
}
$userInfo = (new UserService())->findUserById($userId, ['openid']);
if (!$userInfo) {
throw new ApiException('用户不存在', 500);
}
if (!$userInfo->openid) {
throw new ApiException('请先绑定微信', 500);
}
$payParam = config('wechat.config');
//统一下单参数
$payConfig = [
'appid' => $payParam['appId'],
'mchid' => $payParam['mchId'],
'description' => $orderInfo['price'] . '话费充值-话费慢充72小时内到账',
'out_trade_no' => $orderId,
'notify_url' => $payParam['notifyUrl'],
'amount' => [
'total' => $orderInfo['price'],
'currency' => 'CNY'
],
'payer' => [
'openid' => $userInfo['openid']
]
];
// 重点,微信apiv3必须在headers里设置四个参数
$client = new Client();
$httpClient = $client->post($payParam['payUrl'], [
'headers' => [
'Content-Type' => 'application/json',
'User-Agent' => 'Mozilla/4.0',
'Accept' => 'application/json',
'Authorization' => StringHelper::getAuth($payParam['payUrl'], json_encode($payConfig), 'POST')
],
'json' => $payConfig
]);
$result = $httpClient->getBody()
->getContents();
$payResult = json_decode($result, true);
if ($httpClient->getStatusCode() != 200) {
throw new ApiException('支付错误', 500);
}
if (isset($payResult['errcode'])) {
throw new ApiException($payResult['errmsg'], 500);
}
if (isset($payResult['code'])) {
throw new ApiException($payResult['code'], 500);
}
// 获取支付参数
$appId = $payParam['appId'];
$time = time();
$str = StringHelper::createNonce();
$prepayId = 'prepay_id=' . $payResult['prepay_id'];
$message = $appId . "\n" .
$time . "\n" .
$str . "\n" .
$prepayId . "\n";
return [
'appId' => $payParam['appId'],
'timeStamp' => $time,
'nonceStr' => $str,
'package' => 'prepay_id=' . $payResult['prepay_id'],
'signType' => 'RSA',
'paySign' => StringHelper::getSign($message)
];
}
// 生成字符串
public static function createNonce($length = 32)
{
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
// 生成auth
public static function getAuth($url, $body, $type = 'POST')
{
$config = Config::get('wechat.config');
$nonce = self::createNonce();
$timestamp = time();
$url_parts = parse_url($url);
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
$message = $type . "\n" .
$canonical_url . "\n" .
$timestamp . "\n" .
$nonce . "\n" .
$body . "\n";
$sign = self::getSign($message);
$schema = 'WECHATPAY2-SHA256-RSA2048';
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
$config['mchId'], $nonce, $timestamp, $config['serialNo'], $sign);
return $schema . ' ' . $token;
}
// 获取证书
public static function getSign($message)
{
$filePath = '../resources/cert/apiclient_key.pem';
openssl_sign($message, $raw_sign, openssl_get_privatekey(file_get_contents($filePath)), 'sha256WithRSAEncryption');
return base64_encode($raw_sign);
}
完成支付
这篇文章没有使用微信的sdk,所以代码量还有有点大的,不过整体逻辑简单。