支付

你在阅读本文之前确认你已经仔细阅读了:微信支付 | 商户平台开发文档

网友贡献的教程:小能手马闯set 发布在 Laravel-China 的文章《基于 Laravel5.1 LTS 版的微信开发》

配置

配置在前面的例子中已经提到过了,支付的相关配置如下:

  1. <?php
  2. use EasyWeChat\Foundation\Application;
  3. $options = [
  4. // 前面的appid什么的也得保留哦
  5. 'app_id' => 'xxxx',
  6. // ...
  7. // payment
  8. 'payment' => [
  9. 'merchant_id' => 'your-mch-id',
  10. 'key' => 'key-for-signature',
  11. 'cert_path' => 'path/to/your/cert.pem', // XXX: 绝对路径!!!!
  12. 'key_path' => 'path/to/your/key', // XXX: 绝对路径!!!!
  13. 'notify_url' => '默认的订单回调地址', // 你也可以在下单时单独设置来想覆盖它
  14. // 'device_info' => '013467007045764',
  15. // 'sub_app_id' => '',
  16. // 'sub_merchant_id' => '',
  17. // ...
  18. ],
  19. ];
  20. $app = new Application($options);
  21. $payment = $app->payment;

创建订单

正常模式

  1. <?php
  2. use EasyWeChat\Payment\Order;
  3. $attributes = [
  4. 'trade_type' => 'JSAPI', // JSAPI,NATIVE,APP...
  5. 'body' => 'iPad mini 16G 白色',
  6. 'detail' => 'iPad mini 16G 白色',
  7. 'out_trade_no' => '1217752501201407033233368018',
  8. 'total_fee' => 5388, // 单位:分
  9. 'notify_url' => 'http://xxx.com/order-notify', // 支付结果通知网址,如果不设置则会使用配置里的默认地址
  10. 'openid' => '当前用户的 openid', // trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识,
  11. // ...
  12. ];
  13. $order = new Order($attributes);

子服务商模式

  1. <?php
  2. use EasyWeChat\Payment\Order;
  3. $attributes = [
  4. 'trade_type' => 'JSAPI', // JSAPI,NATIVE,APP...
  5. 'body' => 'iPad mini 16G 白色',
  6. 'detail' => 'iPad mini 16G 白色',
  7. 'out_trade_no' => '1217752501201407033233368018',
  8. 'total_fee' => 5388, // 单位:分
  9. 'notify_url' => 'http://xxx.com/order-notify', // 支付结果通知网址,如果不设置则会使用配置里的默认地址
  10. 'sub_openid' => '当前用户的 openid', // 如果传入sub_openid, 请在实例化Application时, 同时传入$sub_app_id, $sub_merchant_id
  11. // ...
  12. ];
  13. $order = new Order($attributes);

通知url必须为直接可访问的url,不能携带参数。示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action”

下单接口

刷卡支付

官方文档

  1. $result = $payment->pay($order);

也许你需要生成二维码图片,参考页面底部相关的包推荐

统一下单

公众号支付扫码支付APP 支付 都统一使用此接口完成订单的创建。

  1. $result = $payment->prepare($order);
  2. if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS'){
  3. $prepayId = $result->prepay_id;
  4. }

支付结果通知

在用户成功支付后,微信服务器会向该 订单中设置的回调URL 发起一个 POST 请求,请求的内容为一个 XML。里面包含了所有的详细信息,具体请参考:支付结果通用通知

在本 SDK 中处理回调真的再简单不过了,请求验证你就不用管了,SDK 已经为你做好了,你只需要关注业务即可:

  1. $response = $app->payment->handleNotify(function($notify, $successful){
  2. // 你的逻辑
  3. return true; // 或者错误消息
  4. });
  5. $response->send(); // Laravel 里请使用:return $response;

这里需要注意的有几个点:

  1. handleNotify 只接收一个 callable 参数,通常用一个匿名函数即可。
  2. 该匿名函数接收两个参数,这两个参数分别为:

    • $notify 为封装了通知信息的 EasyWeChat\Support\Collection 对象,前面已经讲过这里就不赘述了,你可以以对象或者数组形式来读取通知内容,比如:$notify->total_fee 或者 $notify['total_fee']
    • $successful 这个参数其实就是判断 用户是否付款成功了(result_code == ‘SUCCESS’)
  3. 该函数返回值就是告诉微信 “我是否处理完成”,如果你返回一个 false 或者一个具体的错误消息,那么微信会在稍后再次继续通知你,直到你明确的告诉它:“我已经处理完成了”,在函数里 return true; 代表处理完成。

  4. handleNotify 返回值 $response 是一个 Response 对象,如果你要直接输出,使用 $response->send(), 在一些框架里不是输出而是返回:return $response

通常我们的处理逻辑大概是下面这样(以下只是伪代码):

  1. $response = $app->payment->handleNotify(function($notify, $successful){
  2. // 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
  3. $order = 查询订单($notify->out_trade_no);
  4. if (!$order) { // 如果订单不存在
  5. return 'Order not exist.'; // 告诉微信,我已经处理完了,订单没找到,别再通知我了
  6. }
  7. // 如果订单存在
  8. // 检查订单是否已经更新过支付状态
  9. if ($order->paid_at) { // 假设订单字段“支付时间”不为空代表已经支付
  10. return true; // 已经支付成功了就不再更新了
  11. }
  12. // 用户是否支付成功
  13. if ($successful) {
  14. // 不是已经支付状态则修改为已经支付状态
  15. $order->paid_at = time(); // 更新支付时间为当前时间
  16. $order->status = 'paid';
  17. } else { // 用户支付失败
  18. $order->status = 'paid_fail';
  19. }
  20. $order->save(); // 保存订单
  21. return true; // 返回处理完成
  22. });
  23. return $response;

注意:请把 “支付成功与否” 与 “是否处理完成” 分开,它俩没有必然关系。比如:微信通知你用户支付完成,但是支付失败了(result_code 为 ‘FAIL’),你应该更新你的订单为支付失败,但是要告诉微信处理完成

撤销订单API

目前只有 刷卡支付 有此功能。

调用支付接口后请勿立即调用撤销订单API,建议支付后至少15s后再调用撤销订单接口。

  1. $orderNo = "商户系统内部的订单号(out_trade_no)";
  2. $payment->reverse($orderNo);

或者:

  1. $orderNo = "微信的订单号(transaction_id)";
  2. $payment->reverseByTransactionId($orderNo);

查询订单

该接口提供所有微信支付订单的查询,商户可以通过该接口主动查询订单状态,完成下一步的业务逻辑。

需要调用查询接口的情况:

  • 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知;
  • 调用支付接口后,返回系统错误或未知交易状态情况;
  • 调用被扫支付API,返回USERPAYING的状态;
  • 调用关单或撤销接口API之前,需确认支付状态;
  1. $orderNo = "商户系统内部的订单号(out_trade_no)";
  2. $payment->query($orderNo);

或者:

  1. $orderNo = "微信的订单号(transaction_id)";
  2. $payment->queryByTransactionId($orderNo);

关闭订单

注意:订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟。

  1. $orderNo = "商户系统内部的订单号(out_trade_no)";
  2. $payment->close($orderNo);

申请退款

当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。

注意:

1、交易时间超过一年的订单无法提交退款;2、微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。一笔退款失败后重新提交,要采用原来的退款单号。总退款金额不能超过用户实际支付金额。

  1. $payment->refund(订单号,退款单号,总金额,退款金额,操作员,退款单号类型(out_trade_no/transaction_id),退款账户(REFUND_SOURCE_UNSETTLED_FUNDS/REFUND_SOURCE_RECHARGE_FUNDS))

参考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4

例子:

  1. # 1. 使用商户订单号退款
  2. $result = $payment->refund($orderNo, $refundNo, 100); // 总金额 100 退款 100,操作员:商户号
  3. // or
  4. $result = $payment->refund($orderNo, $refundNo, 100, 80); // 总金额 100, 退款 80,操作员:商户号
  5. // or
  6. $result = $payment->refund($orderNo, $refundNo, 100, 80, 1900000109); // 总金额 100, 退款 80,操作员:1900000109
  7. // or
  8. $result = $payment->refund($orderNo, $refundNo, 100, 80, 1900000109, 'out_trade_no'); // 总金额 100, 退款 80,操作员:1900000109, 退款单号:使用商户订单号退款
  9. // or
  10. $result = $payment->refund($orderNo, $refundNo, 100, 80, 1900000109, 'out_trade_no', 'REFUND_SOURCE_RECHARGE_FUNDS'); // 总金额 100, 退款 80,操作员:1900000109, 退款单号:使用商户订单号退款, 退款账户:可用余额退款
  11. # 2. 使用 TransactionId 退款
  12. $result = $payment->refundByTransactionId($transactionId, $refundNo, 100); // 总金额 100 退款 100,操作员:商户号
  13. // or
  14. $result = $payment->refundByTransactionId($transactionId, $refundNo, 100, 80); // 总金额 100, 退款 80,操作员:商户号
  15. // or
  16. $result = $payment->refundByTransactionId($transactionId, $refundNo, 100, 80, 1900000109); // 总金额 100, 退款 80,操作员:1900000109
  17. // or
  18. $result = $payment->refundByTransactionId($transactionId, $refundNo, 100, 80, 1900000109, 'REFUND_SOURCE_RECHARGE_FUNDS'); // 总金额 100, 退款 80,操作员:1900000109,退款账户:可用余额退款

$refundNo 为商户退款单号,自己生成用于自己识别即可。

查询退款

提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。

  1. $result = $payment->queryRefund($outTradeNo);
  2. // or
  3. $result = $payment->queryRefundByTransactionId($transactionId);
  4. // or
  5. $result = $payment->queryRefundByRefundNo($outRefundNo);
  6. // or
  7. $result = $payment->queryRefundByRefundId($refundId);

下载对账单

  1. $bill = $payment->downloadBill('20140603')->getContents(); // type: ALL
  2. // or
  3. $bill = $payment->downloadBill('20140603', 'SUCCESS')->getContents(); // type: SUCCESS
  4. // bill 为 csv 格式的内容
  5. // 保存为文件
  6. file_put_contents('YOUR/PATH/TO/bill-20140603.csv', $bill);

第二个参数为类型:

  • ALL:返回当日所有订单信息(默认值)
  • SUCCESS:返回当日成功支付的订单
  • REFUND:返回当日退款订单
  • REVOKED:已撤销的订单

测速上报

  1. $payment->report($api, $timeConsuming, $resultCode, $returnCode);
  2. // or
  3. $payment->report($api, $timeConsuming, $resultCode, $returnCode, [
  4. 'err_code' => 'xxxx',
  5. 'err_code_des' => '...',
  6. 'out_trade_no' => '...',
  7. 'user_ip' => '...',
  8. ]);

转换短链接

  1. $shortUrl = $payment->urlShorten('http://easywechat.org');

授权码查询OPENID接口

  1. $response = $payment->authCodeToOpenId($authCode);
  2. $response->openid;

生成支付 JS 配置

有两种发起支付的方式:WeixinJSBridge, JSSDK

  1. WeixinJSBridge:

    1. $json = $payment->configForPayment($prepayId); // 返回 json 字符串,如果想返回数组,传第二个参数 false

    javascript:

    1. ...
    2. WeixinJSBridge.invoke(
    3. 'getBrandWCPayRequest', <?= $json ?>,
    4. function(res){
    5. if(res.err_msg == "get_brand_wcpay_request:ok" ) {
    6. // 使用以上方式判断前端返回,微信团队郑重提示:
    7. // res.err_msg将在用户支付成功后返回
    8. // ok,但并不保证它绝对可靠。
    9. }
    10. }
    11. );
    12. ...
  2. JSSDK:

    1. $config = $payment->configForJSSDKPayment($prepayId); // 返回数组

    javascript:

    1. wx.chooseWXPay({
    2. timestamp: <?= $config['timestamp'] ?>,
    3. nonceStr: '<?= $config['nonceStr'] ?>',
    4. package: '<?= $config['package'] ?>',
    5. signType: '<?= $config['signType'] ?>',
    6. paySign: '<?= $config['paySign'] ?>', // 支付签名
    7. success: function (res) {
    8. // 支付成功后的回调函数
    9. }
    10. });

生成共享收货地址 JS 配置

  1. 发起 OAuth 授权:
  1. use EasyWeChat\Support\Url as UrlHelper;
  2. // 检查当前不是微信 oauth 的回调,则跳过去授权
  3. // 注意,授权回调地址为当前页
  4. if (empty($_GET['code'])) {
  5. $currentUrl = UrlHelper::current(); // 获取当前页 URL
  6. $response = $app->oauth->scopes(['snsapi_base'])->redirect($currentUrl);
  7. return $response; // or echo $response;
  8. }
  9. // 授权回来
  10. $oauthUser = $app->oauth->user();
  11. $token = $oauthUser->getAccessToken();
  12. $configForPickAddress = $payment->configForShareAddress($token);
  13. // 拿着这个生成好的配置 $configForPickAddress 去订单页(或者直接显示订单页)写 js 调用了
  14. // ...

生成 APP 支付配置

  1. $config = $payment->configForAppPayment($prepayId);

$config 为数组格式,你可以用 API 返回给客户端

二维码生成工具推荐

你也许需要生成二维码,那么以下这些供参考: