php 使用wechatpay-apiv3 对接微信App支付

引入包

composer require wechatpay/wechatpay

代码示范

<?php


namespace App\Servers\Pay;

use Illuminate\Support\Facades\Log;
use WeChatPay\Builder;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;
// 开发准备 https://pay.weixin.qq.com/docs/partner/development/development-preparation/parameter-application.html
// 参考代码 https://github.com/wechatpay-apiv3/wechatpay-php (微信官方提供)
class WechatPayServer
{


    protected $wechatPay;

    public function __construct()
    {
        // 商户号 微信支付商户号 获取方式 :https://www.qifeiye.com/%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98%E7%9A%84%E5%95%86%E6%88%B7%E5%8F%B7%E5%92%8C%E5%95%86%E6%88%B7%E5%AF%86%E9%92%A5%EF%BC%9F/
        $merchantId = config('pay.wechat_pay_merchanid');
        // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名  获取方式:商户申请商户API证书时,会生成商户私钥,并保存在本地证书文件夹的文件apiclient_key.pem 中。 文档地址 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml
        $merchantPrivateKeyFilePath = storage_path('wechatPayCertificate/commercial/apiclient_key.pem');
        $merchantPrivateKeyFilePath = 'file://' . $merchantPrivateKeyFilePath;
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
        // 「商户API证书」的「证书序列号」 获取方式 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay7_0.shtml#part-5
        $merchantCertificateSerial = config('pay.wechat_pay_serial');
        // 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名 获取方式1:https://github.com/wechatpay-apiv3/wechatpay-php/tree/main/bin 。还有其他获取方式
        $platformCertificateFilePath = storage_path('wechatPayCertificate/private/wechatpay_cert.pem');
        $platformCertificateFilePath = 'file://' . $platformCertificateFilePath;
        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
        // 从「微信支付平台证书」中获取「证书序列号」
        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
        // 构造一个 APIv3 客户端实例
        $instance = Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs' => [
                $platformCertificateSerial => $platformPublicKeyInstance,
            ],
        ]);

        $this->wechatPay = $instance;
    }

    /**
     * 生成支付签名
     * @param $prepayId
     * @return array
     */
    public function getPaySign($prepayId)
    {
        $merchantPrivateKeyFilePath = storage_path('wechatPayCertificate/commercial/apiclient_key.pem');
        $merchantPrivateKeyFilePath = 'file://' . $merchantPrivateKeyFilePath;
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath);

        $params = [
            'appId' => config('pay.wechat_pay_appid'),
            'timeStamp' => (string)Formatter::timestamp(),
            'nonceStr' => Formatter::nonce(),
            'prepay_id' => $prepayId,
        ];
        $params += ['paySign' => Rsa::sign(
            Formatter::joinedByLineFeed(...array_values($params)),
            $merchantPrivateKeyInstance
        ), 'signType' => 'RSA'];
        return $params;
    }

    /** 创建订单
     * @param $description string  商品描述
     * @param $outTradeNo string 商户系统内部订单号
     * @param $money float 订单总金额,单位为元
     */
    public function createOrder($description, $outTradeNo, $money)
    {
        $money = $money * 100;
        $mchid = config('pay.wechat_pay_merchanid'); // 商户号
        $appid = config('pay.wechat_pay_appid'); // appid
        try {

            $params = ['json' => [
                'appid' => $appid,
                'mchid' => $mchid,
                'out_trade_no' => $outTradeNo,
                'description' => $description,
                'notify_url' => config('app.url') . config('pay.wechat_pay_call_uri'),//回调地址
                'amount' => [
                    'total' => $money,
                    'currency' => 'CNY',
                ]
            ]];
            $resp = $this->wechatPay->chain('v3/pay/transactions/app')->post($params);
            $resp = $resp->getBody()->getContents();
        } catch (\GuzzleHttp\Exception\RequestException $e) {
            $r = $e->getResponse();
            $resp = $r->getBody()->getContents();

            Log::channel('weChatPay')->alert("微信支付创建订单 请求异常", ['StatusCode' => $r->getStatusCode() . ' ' . $r->getReasonPhrase(), 'Body' => $r->getBody(), 'outTradeNo' => $outTradeNo]);
        } catch (\Exception $e) {
            Log::channel('weChatPay')->alert("微信支付创建订单 异常", ['description' => $description, 'outTradeNo' => $outTradeNo, 'money' => $money, 'TraceAsString' => $e->getTraceAsString(), 'msg' => $e->getMessage()]);
            throw $e;
        }
        $res = json_decode($resp, true);
        $paySign = $this->getPaySign($res['prepay_id']);
        $returnData = [
            'appid' => $appid,
            'partnerid' => $mchid,
            'prepayid' => $res['prepay_id'],
            'package' => "Sign=WXPay",
            'noncestr' => $paySign['nonceStr'],
            'timestamp' => $paySign['timeStamp'],
            'sign' => $paySign['paySign'],
        ];

        return $returnData;
    }

    /**
     *解析回调数据
     * @param $requestHead array 请求头
     * @param $data array 获取的请求数据
     */
    public function analysisCallBackData($requestHead, $data)
    {
        $inWechatpaySignature = $requestHead['wechatpay-signature'][0];
        $inWechatpayTimestamp = $requestHead['wechatpay-timestamp'][0];
        $inWechatpayNonce = $requestHead['wechatpay-nonce'][0];
        $inBody = file_get_contents('php://input');// json类型字符串 请根据实际情况获取,例如: file_get_contents('php://input');
        $apiv3Key = config('pay.wechat_pay_apiv3_private_key');// 在商户平台上设置的APIv3密钥
        // 根据通知的平台证书序列号,查询本地平台证书文件
        $wechatpaySerialFilePath = 'file://' . storage_path('wechatPayCertificate/private/wechatpay_cert.pem');
        $platformPublicKeyInstance = Rsa::from($wechatpaySerialFilePath, Rsa::KEY_TYPE_PUBLIC);
        // 检查通知时间偏移量,允许5分钟之内的偏移
        $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);

        if (!$timeOffsetStatus) {//超过5分钟的订单
            Log::channel('weChatPay')->alert('超过5分钟才回调的信息', ['requestHead' => $requestHead, 'requestData' => $data]);
            return false;
        }

        $verifiedStatus = Rsa::verify(
        // 构造验签名串
            Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
            $inWechatpaySignature,
            $platformPublicKeyInstance
        );

        if (!$verifiedStatus) {
            Log::channel('weChatPay')->alert('验证签名失败', ['requestHead' => $requestHead, 'requestData' => $data]);
            return false;
        }

        if ($timeOffsetStatus && $verifiedStatus) {
            // 转换通知的JSON文本消息为PHP Array数组
            $inBodyArray = (array)json_decode($inBody, true);
            // 使用PHP7的数据解构语法,从Array中解构并赋值变量
            ['resource' => [
                'ciphertext' => $ciphertext,
                'nonce' => $nonce,
                'associated_data' => $aad
            ]] = $inBodyArray;
            // 加密文本消息解密
            $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
            // 把解密后的文本转换为PHP Array数组
            $inBodyResourceArray = (array)json_decode($inBodyResource, true);
            return $inBodyResourceArray;
        } else {
            return null;
        }
    }
}

以下是使用 `com.github.wechatpay-apiv3` 库处理微信 H5 支付的 Java 代码示例: ```java import com.github.wechatpay.apiv3.WxPayApiV3; import com.github.wechatpay.apiv3.WxPayApiV3Config; import com.github.wechatpay.apiv3.model.notify.WxPayOrderNotifyResult; import com.github.wechatpay.apiv3.model.notify.WxPayOrderNotifyResult.NotifyResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class WeChatH5NotifyHandler { private static final String WECHAT_API_CERT_SERIAL_NUMBER = "YOUR_WECHAT_API_CERT_SERIAL_NUMBER"; private static final String WECHAT_API_CERTIFICATE_PATH = "path/to/your/wechat/api/certificate.pem"; public void handleNotify(HttpServletRequest request, HttpServletResponse response) throws IOException { try { // 创建微信支付 API 配置 WxPayApiV3Config config = new WxPayApiV3Config.Builder() .appId("your_app_id") .merchantId("your_merchant_id") .privateKeySerialNumber(WECHAT_API_CERT_SERIAL_NUMBER) .privateKeyPath(WECHAT_API_CERTIFICATE_PATH) .build(); // 创建微信支付 API 实例 WxPayApiV3 wxPayApiV3 = new WxPayApiV3(config); // 解析异步通知数据 WxPayOrderNotifyResult notifyResult = wxPayApiV3.parseOrderNotifyResult(request); // 验证签名 if (wxPayApiV3.verifySignature(notifyResult)) { // 签名验证成功 // 处理支付成功的逻辑 // ... // 返回成功响应给微信服务器 response.setStatus(HttpServletResponse.SC_OK); response.getWriter().write("SUCCESS"); } else { // 签名验证失败,返回失败响应给微信服务器 response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.getWriter().write("FAIL"); } } catch (Exception e) { e.printStackTrace(); // 返回失败响应给微信服务器 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.getWriter().write("FAIL"); } } } ``` 在上述代码中,我们创建了一个名为 `WeChatH5NotifyHandler` 的类,其中的 `handleNotify` 方法用于处理微信 H5 支付的异步通知。该方法接收 `HttpServletRequest` 和 `HttpServletResponse` 对象作为参数,从请求中获取异步通知的数据,并进行相应的处理逻辑。 在 `handleNotify` 方法中,我们首先创建了一个 `WxPayApiV3Config` 对象,用于配置微信支付 API 的相关参数。其中,我们需要提供应用 ID(`appId`)、商户号(`merchantId`)、微信支付 API 证书的序列号(`privateKeySerialNumber`)以及证书的路径(`privateKeyPath`)。您需要将这些参数替换为您自己的值。 然后,我们使用 `WxPayApiV3` 实例来解析异步通知数据,并验证签名。如果签名验证成功,则表示支付成功,可以进行相应的处理逻辑,并返回成功响应给微信服务器。如果签名验证失败,则返回失败响应给微信服务器。 请注意,以上示例代码仅供参考,具体的实现可能因应用的需求而有所不同。您需要根据实际情况进行修改和完善。另外,在真实的项目中,请确保您已正确配置和保护微信支付 API 证书的私钥。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值