一文搞懂微信支付 Api-v3 规则实现(附源码) https://developers.weixin.qq.com/community/develop/article/doc/000040ff8642e0555b0afe78951813
https://blog.csdn.net/qq_23564667/article/details/132169681
https://www.cnblogs.com/wjh0521/articles/16740612.html
使用条件
1、商户号已入驻 90 日且截止今日回推 30 天商户号保持连续不间断的交易。
2、登录微信支付商户平台 – 产品中心,开通付款到零钱。
付款资金
付款到零钱资金使用商户号余额资金。
根据商户号的账户开通情况,实际出款账户有做区别:
默认情况下,付款到零钱使用商户号基本户(或余额账户)余额。如商户号已开通运营账户,则付款到零钱使用运营账户内的资金。
基本户(或上述其他出款账户)的资金来源,可能是交易结算款项(仅基本户),或给账户充值的资金。当出款账户余额不足时,付款将因余额不足而付款失败。
1、开通 微信 商家转账到零钱;
。申请api v3秘钥(和api秘钥可以是一样的,需要分开在平台申请);
2、微信sdk安装:composer:composer require wechatpay/wechatpay;
3、证书生成:
4、PHP demo源码:
<?php namespace app\common\extend; use think\Exception; use WeChatPay\Builder; use WeChatPay\Crypto\Rsa; use WeChatPay\Util\PemUtil; /** * 实测 可用 相关配置弄好 * date: 2022/09/19 18:10 * desc: */ class WechatPayV3 { private $v3_key; private $merchantId; private $merchantCertificateSerial; private $merchantPrivateKeyFilePath; private $platformCertificateFilePath; private $platformPublicKeyInstance; private $instance; const AUTH_TAG_LENGTH_BYTE = 16; public function __construct() { //证书生成命令(生成的证书在sdk目录 或 自己指定) //php ./bin/CertificateDownloader.php -k jiaxin**************uzhou20 -m 1614781491 -f /www/wwwroot/项目目录/server/public/cert/apiclient_key.pem -s 629BD177D7******************8CE43244 -o ./ $pay_config = PayConfig::find(2); $pay_config = $pay_config->config; // 商户号 $this->merchantId = $pay_config['mch_id']; // v3api秘钥 $this->v3_key = $pay_config['pay_sign_key']; // 「商户API证书」的「证书序列号」 商户平台【API安全】->【API证书】->【查看证书】,可查看商户API证书序列号 $this->merchantCertificateSerial = '629BD177D7BB33*******13620F8CE43244'; // 商户API私钥 //$this->merchantPrivateKeyFilePath = $pay_config['apiclient_key']; //"file://" $this->merchantPrivateKeyFilePath = "file://" . root_path() . "public/upload/cert/apiclient_key.pem"; // ; // 微信支付平台证书 首次通过命令行生成 https://github.com/wechatpay-apiv3/wechatpay-php/tree/main/bin //$this->platformCertificateFilePath = $pay_config['apiclient_cert']; $this->platformCertificateFilePath = "file://" . root_path() . "public/upload/cert/wechatpay_547228274D64DE93A481DEAB6F44CC22CB604784.pem"; // 构建客户端实例 $this->instance = $this->buildInstance(); //var_dump($this->merchantPrivateKeyFilePath,$this->platformCertificateFilePath);exit; } /** * 商家付款到零钱 * @param string $appid 申请商户号的appid或商户号绑定的appid(企业号corpid即为此appid),小程序、公众号、APP * @param string $batch_sn 商户系统内部的商家批次单号 * @param string $order_sn 账明细单的唯一标识 * @param float $money 转账金额,单位:元 * @param string $openid 用户openid * @param string $remark 转账备注 * @param string $user_name 用户姓名,转账金额大于2000元,必填 * @return array * @throws Exception */ public function transfer(string $appid, string $batch_sn, string $order_sn, float $money, string $openid, string $remark, string $user_name = ''): array { $trans_detail = [ 'out_detail_no' => $order_sn, 'transfer_amount' => $money * 100, 'transfer_remark' => $remark, 'openid' => $openid, ]; if ($money >= 2000) { if (empty($user_name)) { throw new Exception('提现金额大于2000元,真实姓名不能为空'); } $trans_detail['user_name'] = $this->encrypt($user_name); } $response = $this->instance->chain('v3/transfer/batches')->post([ 'json' => [ 'appid' => $appid, 'out_batch_no' => $batch_sn, 'batch_name' => '提现转账', 'batch_remark' => '提现转账', 'total_amount' => $money * 100, 'total_num' => 1, 'transfer_detail_list' => [ $trans_detail ] ] ]); return json_decode($response->getBody(), true); } /** * 转账明细查询 * @param string $out_batch_no 商户系统内部的商家批次单号 * @param string $out_detail_no 账明细单的唯一标识 * @return array */ public function transferQuery(string $out_batch_no, string $out_detail_no = ''): array { $api = 'v3/transfer/batches/out-batch-no/' . $out_batch_no; $query = []; if ($out_detail_no) { $api .= '/details/out-detail-no/' . $out_detail_no; } else { $query = [ 'query' => [ 'need_query_detail' => true, 'detail_status' => 'ALL' ] ]; } $response = $this->instance->chain($api)->get($query); return json_decode($response->getBody(), true); } // 自动更新平台证书 public function autoUpdatePlatformCertificateFile() { $result = $this->v3Certificates()['data']; $data = end($result); $effective_time = strtotime($data['effective_time']); if ($effective_time - time() > 0 && $effective_time - time() <= 86400) { $encrypt_certificate = $data['encrypt_certificate']; $cert_content = $this->decryptToString($encrypt_certificate['associated_data'], $encrypt_certificate['nonce'], $encrypt_certificate['ciphertext']); file_exists($this->platformCertificateFilePath) && file_put_contents($this->platformCertificateFilePath, $cert_content); } } // 获取平台证书 private function v3Certificates() { $response = $this->instance->chain('v3/certificates')->get(); return json_decode($response->getBody(), true); } /** * Decrypt AEAD_AES_256_GCM ciphertext * 用于解密v3Certificates接口数据获取证书 * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_2.shtml * @param string $associatedData AES GCM additional authentication data * @param string $nonceStr AES GCM nonce * @param string $ciphertext AES GCM cipher text * * @return string|bool Decrypted string on success or FALSE on failure */ private function decryptToString($associatedData, $nonceStr, $ciphertext) { $ciphertext = \base64_decode($ciphertext); if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) { return false; } // ext-sodium (default installed on >= PHP 7.2) if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) { return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->v3_key); } // ext-libsodium (need install libsodium-php 1.x via pecl) if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) { return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->v3_key); } // openssl (PHP >= 7.1 support AEAD) if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) { $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE); $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE); return \openssl_decrypt($ctext, 'aes-256-gcm', $this->v3_key, \OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData); } throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php'); } // 加密 private function encrypt(string $msg): string { return Rsa::encrypt($msg, $this->platformPublicKeyInstance); } private function buildInstance(): \WeChatPay\BuilderChainable { // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名 $merchantPrivateKeyInstance = Rsa::from($this->merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE); // 从本地文件中加载 微信支付平台证书 $this->platformPublicKeyInstance = Rsa::from($this->platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC); // 从「微信支付平台证书」中获取「证书序列号」 $platformCertificateSerial = PemUtil::parseCertificateSerialNo($this->platformCertificateFilePath); // 构造一个 APIv3 客户端实例 return Builder::factory([ 'mchid' => $this->merchantId, 'serial' => $this->merchantCertificateSerial, 'privateKey' => $merchantPrivateKeyInstance, 'certs' => [ $platformCertificateSerial => $this->platformPublicKeyInstance, ], ]); } }