Skip to content

支付

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

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

配置

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

php
<?php

use EasyWeChat\Foundation\Application;

$options = [
    // 前面的appid什么的也得保留哦
    'app_id' => 'xxxx',
    // ...

    // payment
    'payment' => [
        'merchant_id'        => 'your-mch-id',
        'key'                => 'key-for-signature',
        'cert_path'          => 'path/to/your/cert.pem', // XXX: 绝对路径!!!!
        'key_path'           => 'path/to/your/key',      // XXX: 绝对路径!!!!
        'notify_url'         => '默认的订单回调地址',       // 你也可以在下单时单独设置来想覆盖它
        // 'device_info'     => '013467007045764',
        // 'sub_app_id'      => '',
        // 'sub_merchant_id' => '',
        // ...
    ],
];

$app = new Application($options);

$payment = $app->payment;

创建订单

正常模式

php
<?php

use EasyWeChat\Payment\Order;

$attributes = [
    'trade_type'       => 'JSAPI', // JSAPI,NATIVE,APP...
    'body'             => 'iPad mini 16G 白色',
    'detail'           => 'iPad mini 16G 白色',
    'out_trade_no'     => '1217752501201407033233368018',
    'total_fee'        => 5388, // 单位:分
    'notify_url'       => 'http://easywechat.com/order-notify', // 支付结果通知网址,如果不设置则会使用配置里的默认地址
    'openid'           => '当前用户的 openid', // trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识,
    // ...
];

$order = new Order($attributes);

子服务商模式

php
<?php

use EasyWeChat\Payment\Order;

$attributes = [
    'trade_type'       => 'JSAPI', // JSAPI,NATIVE,APP...
    'body'             => 'iPad mini 16G 白色',
    'detail'           => 'iPad mini 16G 白色',
    'out_trade_no'     => '1217752501201407033233368018',
    'total_fee'        => 5388, // 单位:分
    'notify_url'       => 'http://easywechat.com/order-notify', // 支付结果通知网址,如果不设置则会使用配置里的默认地址
    'sub_openid'        => '当前用户的 openid', // 如果传入sub_openid, 请在实例化Application时, 同时传入$sub_app_id, $sub_merchant_id
    // ...
];

$order = new Order($attributes);

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

下单接口

刷卡支付

官方文档

php
$result = $payment->pay($order);

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

统一下单

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

php
$result = $payment->prepare($order);
if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS'){
    $prepayId = $result->prepay_id;
}

支付结果通知

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

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

php
$response = $app->payment->handleNotify(function($notify, $successful){
    // 你的逻辑
    return true; // 或者错误消息
});

$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

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

php
$response = $app->payment->handleNotify(function($notify, $successful){
    // 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
    $order = 查询订单($notify->out_trade_no);

    if (!$order) { // 如果订单不存在
        return 'Order not exist.'; // 告诉微信,我已经处理完了,订单没找到,别再通知我了
    }

    // 如果订单存在
    // 检查订单是否已经更新过支付状态
    if ($order->paid_at) { // 假设订单字段“支付时间”不为空代表已经支付
        return true; // 已经支付成功了就不再更新了
    }

    // 用户是否支付成功
    if ($successful) {
        // 不是已经支付状态则修改为已经支付状态
        $order->paid_at = time(); // 更新支付时间为当前时间
        $order->status = 'paid';
    } else { // 用户支付失败
        $order->status = 'paid_fail';
    }

    $order->save(); // 保存订单

    return true; // 返回处理完成
});

return $response;

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

撤销订单 API

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

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

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

或者:

php

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

查询订单

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

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

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

或者:

php

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

关闭订单

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

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

申请退款

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

注意:

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

php
$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

例子:

php
# 1. 使用商户订单号退款
$result = $payment->refund($orderNo, $refundNo, 100); // 总金额 100 退款 100,操作员:商户号
// or
$result = $payment->refund($orderNo, $refundNo, 100, 80); // 总金额 100, 退款 80,操作员:商户号
// or
$result = $payment->refund($orderNo, $refundNo, 100, 80, 1900000109); // 总金额 100, 退款 80,操作员:1900000109
// or
$result = $payment->refund($orderNo, $refundNo, 100, 80, 1900000109, 'out_trade_no'); // 总金额 100, 退款 80,操作员:1900000109, 退款单号:使用商户订单号退款
// or
$result = $payment->refund($orderNo, $refundNo, 100, 80, 1900000109, 'out_trade_no', 'REFUND_SOURCE_RECHARGE_FUNDS'); // 总金额 100, 退款 80,操作员:1900000109, 退款单号:使用商户订单号退款, 退款账户:可用余额退款

# 2. 使用 TransactionId 退款
$result = $payment->refundByTransactionId($transactionId, $refundNo, 100); // 总金额 100 退款 100,操作员:商户号
// or
$result = $payment->refundByTransactionId($transactionId, $refundNo, 100, 80); // 总金额 100, 退款 80,操作员:商户号
// or
$result = $payment->refundByTransactionId($transactionId, $refundNo, 100, 80, 1900000109); // 总金额 100, 退款 80,操作员:1900000109
// or
$result = $payment->refundByTransactionId($transactionId, $refundNo, 100, 80, 1900000109, 'REFUND_SOURCE_RECHARGE_FUNDS'); // 总金额 100, 退款 80,操作员:1900000109,退款账户:可用余额退款

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

查询退款

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

php
$result = $payment->queryRefund($outTradeNo);
// or
$result = $payment->queryRefundByTransactionId($transactionId);
// or
$result = $payment->queryRefundByRefundNo($outRefundNo);
// or
$result = $payment->queryRefundByRefundId($refundId);

下载对账单

php
$bill = $payment->downloadBill('20140603')->getContents(); // type: ALL
// or
$bill = $payment->downloadBill('20140603', 'SUCCESS')->getContents(); // type: SUCCESS
// bill 为 csv 格式的内容

// 保存为文件
file_put_contents('YOUR/PATH/TO/bill-20140603.csv', $bill);

第二个参数为类型:

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

测速上报

php
$payment->report($api, $timeConsuming, $resultCode, $returnCode);
// or
$payment->report($api, $timeConsuming, $resultCode, $returnCode, [
        'err_code'     => 'xxxx',
        'err_code_des' => '...',
        'out_trade_no' => '...',
        'user_ip'      => '...',
    ]);

转换短链接

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

授权码查询 OPENID 接口

php
$response = $payment->authCodeToOpenId($authCode);
$response->openid;

生成支付 JS 配置

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

  1. WeixinJSBridge:

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

    javascript:

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

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

    javascript:

    js
    wx.chooseWXPay({
        timestamp: <?= $config['timestamp'] ?>,
        nonceStr: '<?= $config['nonceStr'] ?>',
        package: '<?= $config['package'] ?>',
        signType: '<?= $config['signType'] ?>',
        paySign: '<?= $config['paySign'] ?>', // 支付签名
        success: function (res) {
            // 支付成功后的回调函数
        }
    });

生成共享收货地址 JS 配置

  1. 发起 OAuth 授权:
php
use EasyWeChat\Support\Url as UrlHelper;

// 检查当前不是微信 oauth 的回调,则跳过去授权
// 注意,授权回调地址为当前页
if (empty($_GET['code'])) {
    $currentUrl = UrlHelper::current(); // 获取当前页 URL
    $response = $app->oauth->scopes(['snsapi_base'])->redirect($currentUrl);

    return $response; // or echo $response;

}
// 授权回来
$oauthUser = $app->oauth->user();
$token = $oauthUser->getAccessToken();
$configForPickAddress = $payment->configForShareAddress($token);

// 拿着这个生成好的配置 $configForPickAddress 去订单页(或者直接显示订单页)写 js 调用了
// ...

生成 APP 支付配置

php
$config = $payment->configForAppPayment($prepayId);

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

二维码生成工具推荐

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

Released under the MIT License.