Skip to content

Commit e4473e0

Browse files
author
Yang Guoshuai
committed
微信支付文档
1 parent b864a78 commit e4473e0

File tree

2 files changed

+270
-8
lines changed

2 files changed

+270
-8
lines changed

docs/WxPay.md

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
微信支付对接
2+
------
3+
4+
# 使用说明
5+
在使用前,请先参考微信支付商户的普通商户的[开发文档][1],具体的接口说明,以微信的文档为准。
6+
7+
# WxPay为你提供了:
8+
* 自动处理`开发者``微信服务器`请求的请求/响应格式
9+
* 自动加上对应的请求签名
10+
* 自动处理`微信服务器``开发者`回调的请求/响应
11+
* 自动解密微信回调数据
12+
* 安全回调(防止xml 实体攻击)
13+
* 关键api实现
14+
* 统一下单
15+
* 关单
16+
* 退款
17+
* 订单查询
18+
* 退款查询
19+
* 回调action自动注入
20+
* 支付回调
21+
* 退款回调
22+
23+
理论上,一切和微信服务器对接相关的脏活累活,WxPay都为你实现了,你需要关注你的业务逻辑即可。
24+
25+
# 准备工作
26+
本文以公众号支付为基础,介绍公众号支付,扫码支付,小程序支付,H5支付,APP支付等常用的支付手段,
27+
以及退款/订单查询等在支付过程中必要的接口调用。
28+
29+
# 支付申请
30+
使用微信公众号支付,你需要:
31+
* 可用的微信公众号(服务号)
32+
* 和服务号同主体的微信商户开通
33+
34+
在此基础上,可以开通:
35+
* 扫码支付
36+
* H5支付
37+
38+
如果你有一个小程序,在小程序关联微信支付商户之后,你可以:
39+
* 小程序支付
40+
41+
APP支付需要单独申请(申请见[微信开放平台][2]),与公众号支付无法公用一个微信商户
42+
43+
# 后台对接
44+
45+
* 配置注入实例
46+
在yii2的web配置中,加入如下的配置,推荐放入至common的`main-local.php`中,如 `common/config/main-local.php`中。
47+
48+
```php
49+
return [
50+
'components' => [
51+
//其它配置
52+
//此处配置要注入的Wxpay实例,key可以随便写。建议写wxpay
53+
'wxpay' => [
54+
'class' => '\lspbupt\wechat\WxPay',
55+
'payappid' => '商户对应的公众号id',
56+
'mch_id' => '商户id',
57+
'mch_key' => '商户密钥',
58+
'apiclient_cert' => '@common/cert/apiclient_cert.pem',
59+
'apiclient_key' => '@common/cert/apiclient_key.pem',
60+
'notify_url' => 'https://url.to/pay/callback',
61+
],
62+
],
63+
//....
64+
];
65+
```
66+
67+
## 配置获取说明
68+
* payappid 公众号的appid
69+
* 因为一个商户对应的微信服务可能有很多个,默认是公众号的appid
70+
* 在小程序支付需要手动修改小程序的appid
71+
* mch_id/mch_key 商户api密钥,见[支付账户][3]
72+
* apiclient_cert/apiclient_key 商户api证书
73+
* 路径支持yii2别名
74+
* 申请见[安全规范][4]
75+
* 回调地址
76+
* 默认的处理回调通知的地址
77+
78+
# API调用
79+
## 基本调用
80+
[Wechat](Wechat.md)
81+
## 可选参数机制
82+
当我们调用WxPay的公有实例方法的时候,有部分参数选择一个提供即可,api本身会变得比较冗长
83+
因此,我们引入了可选参数函数,让开发者能够方便的传入接口api调用的必要参数。
84+
### `Wechat::setOptional`
85+
```php
86+
public function setOptional(string $key, $value): self;
87+
Yii::$app->wechat->setOptional('out_trade_no', 'foo|bar|123')->orderquery();
88+
Yii::$app->wechat->setOptional('transaction_id', '12312321212')->orderquery();
89+
```
90+
91+
如果每个可选参数都没有提供,将会抛出异常。
92+
93+
## 统一下单
94+
```php
95+
public function unifiedorder($body, $tradeNo, $totalFee, array $params = [])
96+
```
97+
微信文档见[jsapi][5]
98+
99+
API参数对应微信文档
100+
* body 商品描述 body
101+
* tradeNo 微信的out_trade_no
102+
* totalFee 单位分 totalFee
103+
* params 对于不同的支付方式的补充参数不一样
104+
* jsapi(公众号支付,小程序支付)
105+
```php
106+
[
107+
'trade_type' => WxPay::PAY_TRADE_TYPE_JSAPI,
108+
'spbill_create_ip' => $remoteIp,//支付用户的remoteip,
109+
'openid' => $openid,//支付用户的openid
110+
];
111+
```
112+
支付用户的openid,获取见[微信登录](WxLogin.md)
113+
注意,如果给用户更好的体验,最好用静默登录
114+
如果小程序支付,首先调用WxPay::setPayAppid()设置小程序的appid之后再下单;微信支付小程序的用户openid,见[WxSmallApp文档](WxSmallApp.md)
115+
* 扫码支付
116+
```php
117+
[
118+
'trade_type' => WxPay::PAY_TRADE_TYPE_NATIVE,
119+
'product_id' => ‘PRODUCT_123',//商品id,可以用out_trade_no或其他逻辑。
120+
];
121+
```
122+
* H5支付
123+
```php
124+
'trade_type' => WxPay::PAY_TRADE_TYPE_MWEB,
125+
'spbill_create_ip' => $remoteIp,//支付用户的remoteip,
126+
'scene_info' => json_encode([
127+
'h5_info' => 'ANDROID, XXXAPP',//上报的app信息
128+
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
129+
```
130+
131+
返回值和错误
132+
* 返回`$ret`统一转换为数组,具体规范见[jsapi][5]
133+
* xml解析之后的数据放在`$ret['parsed']`中
134+
* 最关键的是prepayId,留作后续支付用
135+
## 用户支付
136+
### JSAPI支付
137+
利用WxPay::buildJsapiParams(string $prepayId): array方法得到支付参数之后,传给前端调起支付。
138+
调起支付的示例代码:
139+
```javascript
140+
function onBridgeReady(){
141+
WeixinJSBridge.invoke(
142+
'getBrandWCPayRequest', <?=json_encode($jsapiParams, JSON_PRETTY_PRINT); ?>,
143+
function(res){
144+
if(res.err_msg == "get_brand_wcpay_request:ok") {
145+
//处理已支付
146+
return;
147+
} else if (res.err_msg == "get_brand_wcpay_request:cancel") {
148+
//处理取消支付
149+
return;
150+
} else if (res.errMsg == "chooseWXPay:fail, the permission value is offline verifying") {
151+
console.log("微信支付将在真机调起。");
152+
alert("微信支付将在真机调起。");
153+
return;
154+
}
155+
return;
156+
}
157+
);
158+
}
159+
if (typeof WeixinJSBridge == "undefined"){
160+
if( document.addEventListener ){
161+
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
162+
}else if (document.attachEvent){
163+
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
164+
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
165+
}
166+
} else {
167+
onBridgeReady();
168+
}
169+
```
170+
### NATIVE(扫码支付)
171+
将返回的`$ret['data']['parsed']['code_url']`展示给用户引导用户付款
172+
173+
### H5支付
174+
将返回的`$ret['data']['parsed']['mweb_url']`交给前端在合适的时候跳转
175+
176+
#### 在android/ios APP 嵌入H5支付
177+
后续补充。
178+
## 订单查询
179+
WxPay::orderquery()
180+
181+
可选参数:out_trade_no/transaction_id
182+
## 关单
183+
WxPay::closeOrder($tradeNo)
184+
185+
因为关闭的订单一定还没有支付,所以没有流水号可选。
186+
## 退款
187+
WxPay::refund($refundNo, int $totalFee, int $refundFee)
188+
189+
refundNo: 唯一退款号,自行生成
190+
191+
totalFee: 支付总金额
192+
193+
refundFee: 退款金额
194+
195+
## 退款查询
196+
WxPay::refundquery()
197+
198+
可选参数:out_trade_no/transaction_id/out_refund_no/refund_id 之一即可
199+
200+
注意,如果用支付参数查询,会查到多笔退款
201+
202+
# 回调处理
203+
在需要处理的controller注入回调方法
204+
```php
205+
class FooController extends Controller
206+
{
207+
public function actions()
208+
{
209+
return [
210+
'bar' => [
211+
'class' => Yii::$app->wxpay->callbackAction(function ($post) {
212+
if(isset($post['refund_id'])) {
213+
$result = Baz::processAfterRefund($post);
214+
} else {
215+
$result = Boo::processAfterPay($post);
216+
}
217+
if ($result) {
218+
return [
219+
'return_code' => 'SUCCESS',
220+
'return_msg' => 'OK',
221+
];
222+
}
223+
return [
224+
'return_code' => 'FAIL',
225+
'return_msg' => '处理错误',
226+
];
227+
],
228+
];
229+
}
230+
}
231+
```
232+
当程序错误或者我们手动返回处理失败的时候,微信服务器会进行多次重试,请及时处理。
233+
支付结果通知的格式见[支付结果通知][6]
234+
退款结果通知的格式见[退款结果通知][7],已完成对加密数据的解密工作。
235+
236+
[1]: https://pay.weixin.qq.com/wiki/doc/api/index.html
237+
[2]: https://open.weixin.qq.com
238+
[3]: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=3_1
239+
[4]: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
240+
[5]: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
241+
[6]: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
242+
[7]: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=10
243+
244+
245+
# 附录
246+
##为啥要用可选参数?
247+
如果不用,我们的api和调用会长成这个样子:
248+
249+
### api:
250+
```php
251+
public function orderquery($out_trade_no = '', $transaction_id = '') {
252+
$params = [];
253+
if($out_trade_no) {
254+
$params['out_trade_no'] = $out_trade_no;
255+
} else if ($transaction_id) {
256+
$params['transaction_id'] = $transaction_id;
257+
} else {
258+
throw Exception();
259+
}
260+
#...
261+
}
262+
```
263+
### call:
264+
```php
265+
Yii::$app->wechat->orderquery('foo|bar|123');
266+
Yii::$app->wechat->orderquery('', '1231123111');
267+
```
268+
269+
如果有多个可选参数,需要一个即可,无论是调用还是api实现都将成为噩梦。

src/WxPay.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use Yii;
66
use Closure;
77
use InvalidArgumentException;
8-
use yii\di\Instance;
98
use yii\web\Response;
109
use yii\base\Action;
1110
use yii\base\InvalidConfigException;
@@ -51,10 +50,6 @@ class WxPay extends \lspbupt\curl\CurlHttp
5150
* @var string 回调地址
5251
*/
5352
public $notify_url;
54-
/**
55-
* @var WxApp 微信组件,包含appid和appSecret
56-
*/
57-
public $wxapp = 'wxapp';
5853

5954
private $optional = [];
6055
/**
@@ -65,15 +60,13 @@ class WxPay extends \lspbupt\curl\CurlHttp
6560
public function init()
6661
{
6762
parent::init();
68-
$this->wxapp = Instance::ensure(Yii::$app->{$this->wxapp}, WxApp::class);
69-
$this->payappid = $this->wxapp->appid;
7063
$this->beforeRequest = Closure::fromCallable([$this, 'beforeRequest']);
7164
$this->afterRequest = Closure::fromCallable([$this, 'afterRequest']);
7265
}
7366

7467
public function setPayAppid($appid)
7568
{
76-
$appid && $this->payappid = $appid;
69+
$this->payappid = $appid;
7770
}
7871

7972
public function buildJsapiParams(string $prepayId): array

0 commit comments

Comments
 (0)