您好,欢迎访问一九零五行业门户网

随着微信支付的升级,PHP微信支付类V3接口也来了

不知不觉微信支付也更新了,接口版本也升级到了v3,跟着微信的升级,将个人使用微信支付类也进行了升级,v3微信支付文档:https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml。
使用方法还和之前的一样(v2微信支付),直接传递参数就可使用:
新版新增了composer安装,便于集成框架使用(github地址):
composer require fengkui/pay
首先把配置文件填写完整(细心不要填错,否则会导致签名错误):
# 微信支付配置$wechatconfig = [ 'xcxid' => '', // 小程序 appid 'appid' => '', // 微信支付 appid 'mchid' => '', // 微信支付 mch_id 商户收款账号 'key' => '', // 微信支付 apiv3key(尽量包含大小写字母,否则验签不通过) 'appsecret' => '', // 公众帐号 secert (公众号支付获取 code 和 openid 使用) 'notify_url' => '', // 接收支付状态的连接 改成自己的回调地址 'redirect_url' => '', // 公众号支付,调起支付页面 'serial_no' => '', // 证书序列号 'cert_client' => './cert/apiclient_cert.pem', // 证书(退款,红包时使用) 'cert_key' => './cert/apiclient_key.pem', // 商户私钥(api安全中下载) 'public_key' => './cert/public_key.pem', // 平台公钥(调动证书列表,自动生成)];
支付类封装相关方法:
method描述
js jsapi下单
app app支付
h5 h5支付
scan navicat支付
xcx 小程序支付
query 查询订单
close 关闭订单
refund 申请退款
notify 支付结果通知
使用方法(这里已小程序支付为示例):
<?phprequire_once('./vendor/autoload.php');$config = []; // 支付配置$order = [ 'order_sn' => time(), // 订单编号 'total_amount' => 1, // 订单金额(分) 'body' => '测试商品', // 商品名称 'openid' => '', // 用户openid // 'type' => 'wap',];$wechat = new fengkui\pay\wechat($config);$re = $wechat->xcx($order);die(json_encode($re)); // json化直接返回小程序客户端
如下代码是封装好的完整支付类文件(wechat.php),
可以根据自己需求随意修改,详细的使用方法后期会有文档:
<?php/** * @author: [feng] <1161634940@qq.com> * @date: 2019-09-06 09:50:30 * @last modified by: [feng] <1161634940@qq.com> * @last modified time: 2021-07-12 18:24:18 */namespace fengkui\pay;use exception;use runtimeexception;use fengkui\supports\http;/** * wechat 微信支付 * 新版(v3)接口(更新中) */class wechat{ const auth_tag_length_byte = 16; // 新版相关接口 // get 获取平台证书列表 private static $certificatesurl = 'https://api.mch.weixin.qq.com/v3/certificates'; // 统一下订单管理 private static $transactionsurl = 'https://api.mch.weixin.qq.com/v3/pay/transactions/'; // 申请退款 private static $refundurl = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds'; // 静默授权,获取code private static $authorizeurl = 'https://open.weixin.qq.com/connect/oauth2/authorize'; // 通过code获取access_token以及openid private static $accesstokenurl = 'https://api.weixin.qq.com/sns/oauth2/access_token'; // 支付完整配置 private static $config = array( 'xcxid' => '', // 小程序appid 'appid' => '', // 微信支付appid 'mchid' => '', // 微信支付 mch_id 商户收款账号 'key' => '', // 微信支付 apiv3key(尽量包含大小写字母,否则验签不通过) 'appsecret' => '', // 公众帐号 secert (公众号支付获取code 和 openid使用) 'notify_url' => '', // 接收支付状态的连接 改成自己的回调地址 'redirect_url' => '', // 公众号支付,调起支付页面 'serial_no' => '', // 证书序列号 'cert_client' => './cert/apiclient_cert.pem', // 证书(退款,红包时使用) 'cert_key' => './cert/apiclient_key.pem', // 商户私钥(api安全中下载) 'public_key' => './cert/public_key.pem', // 平台公钥(调动证书列表,自动生成) ); /** * [__construct 构造函数] * @param [type] $config [传递微信支付相关配置] */ public function __construct($config=null, $referer=null){ $config && self::$config = array_merge(self::$config, $config); } /** * [unifiedorder 统一下单] * @param [type] $order [订单信息(必须包含支付所需要的参数)] * @param boolean $type [区分是否是小程序,是则传 true] * @return [type] [description] * $order = array( * 'body' => '', // 产品描述 * 'order_sn' => '', // 订单编号 * 'total_amount' => '', // 订单金额(分) * ); */ public static function unifiedorder($order, $type=false) { $config = array_filter(self::$config); // 获取配置项 $params = array( 'appid' => $type ? $config['xcxid'] : $config['appid'], // 由微信生成的应用id 'mchid' => $config['mchid'], // 直连商户的商户号 'description' => $order['body'], // 商品描述 'out_trade_no' => (string)$order['order_sn'], // 商户系统内部订单号 'notify_url' => $config['notify_url'], // 通知url必须为直接可访问的url 'amount' => ['total' => (int)$order['total_amount'], 'currency' => 'cny'], // 订单金额信息 ); !empty($order['attach']) && $params['attach'] = $order['attach']; // 附加数据 if (!empty($order['time_expire'])) { // 订单失效时间 preg_match('/[年\/-]/', $order['time_expire']) && $order['time_expire'] = strtotime($order['time_expire']); $time = $order['time_expire'] > time() ? $order['time_expire'] : $order['time_expire'] + time(); $params['time_expire'] = date(date_atom, $time); } if (!in_array($order['type'], ['native'])) { !empty($order['openid']) && $params['payer'] = ['openid' => $order['openid']]; $params['scene_info'] = ['payer_client_ip' => self::get_ip()]; } if (in_array($order['type'], ['ios', 'android', 'wap'])) { $params['scene_info']['h5_info'] = ['type' => $order['type']]; $url = self::$transactionsurl . 'h5'; // 拼接请求地址 } else { $url = self::$transactionsurl . strtolower($order['type']); // 拼接请求地址 } $header = self::createauthorization($url, $params, 'post'); $response = http::post($url, json_encode($params, json_unescaped_unicode), $header); $result = json_decode($response, true); if (isset($result['code']) && isset($result['message'])) { throw new \exception("[" . $result['code'] . "] " . $result['message']); } return $result; } /** * [query 查询订单] * @param [type] $ordersn [订单编号] * @param boolean $type [微信支付订单编号,是否是微信支付订单号] * @return [type] [description] */ public static function query($ordersn, $type = false) { $config = self::$config; $url = self::$transactionsurl . ($type ? 'id/' : 'out-trade-no/') . $ordersn . '?mchid=' . $config['mchid']; $params = ''; $header = self::createauthorization($url, $params, 'get'); $response = http::get($url, $params, $header); $result = json_decode($response, true); return $result; } /** * [close 关闭订单] * @param [type] $ordersn [微信支付订单编号] * @return [type] [description] */ public static function close($ordersn) { $config = self::$config; $url = self::$transactionsurl . 'out-trade-no/' . $ordersn . '/close'; $params['mchid'] = $config['mchid']; $header = self::createauthorization($url, $params, 'post'); $response = http::post($url, json_encode($params, json_unescaped_unicode), $header); $result = json_decode($response, true); return true; } /** * [js 获取jssdk需要用到的数据] * @param [type] $order [订单信息数组] * @return [type] [description] */ public static function js($order=[]){ $config = self::$config; if (!is_array($order) || count($order) < 3) die("订单数组信息缺失!"); if (count($order) == 4 && !empty($order['openid'])) { $data = self::xcx($order, false, false); // 获取支付相关信息(获取非小程序信息) return $data; } $code = !empty($order['code']) ? $order['code'] : ($_get['code'] ''); $redirecturi = $_server['request_scheme'] . '://' . $_server['http_host'] . rtrim($_server['request_uri'], '/') . '/'; // 重定向地址 $params = ['appid' => $config['appid']]; // 如果没有get参数没有code;则重定向去获取code; if (empty($code)) { $params['redirect_uri'] = $redirecturi; // 返回的url $params['response_type'] = 'code'; $params['scope'] = 'snsapi_base'; $params['state'] = $order['order_sn']; // 获取订单号 $url = self::$authorizeurl . '?'. http_build_query($params) .'#wechat_redirect'; } else { $params['secret'] = $config['appsecret']; $params['code'] = $code; $params['grant_type'] = 'authorization_code'; $response = http::get(self::$accesstokenurl, $params); // 进行get请求 $result = json_decode($response, true); $order['openid'] = $result['openid']; // 获取到的openid $data = self::xcx($order, false, false); // 获取支付相关信息(获取非小程序信息) if (!empty($order['code'])) { return $data; } $url = $config['redirect_url'] $redirecturi; $url .= '?data=' . json_encode($data, json_unescaped_unicode); } header('location: '. $url); die; } /** * [app 获取app支付需要用到的数据] * @param [type] $order [订单信息数组] * @return [type] [description] */ public static function app($order=[], $log=false) { if(empty($order['order_sn']) || empty($order['total_amount']) || empty($order['body'])){ die("订单数组信息缺失!"); } $order['type'] = 'app'; // 获取订单类型,用户拼接请求地址 $result = self::unifiedorder($order, true); if (!empty($result['prepay_id'])) { $data = array ( 'appid' => self::$config['appid'], // 微信开放平台审核通过的移动应用appid 'timestamp' => (string)time(), 'noncestr' => self::get_rand_str(32, 0, 1), // 随机32位字符串 'prepayid' => $result['prepay_id'], ); $data['paysign'] = self::makesign($data); $data['partnerid'] = $config['mchid']; $data['package'] = 'sign=wxpay'; return $data; // 数据小程序客户端 } else { return $log ? $result : false; } } /** * [h5 微信h5支付] * @param [type] $order [订单信息数组] * @return [type] [description] */ public static function h5($order=[], $log=false) { if(empty($order['order_sn']) || empty($order['total_amount']) || empty($order['body']) || empty($order['type']) || !in_array(strtolower($order['type']), ['ios', 'android', 'wap'])){ die("订单数组信息缺失!"); } $result = self::unifiedorder($order); if (!empty($result['h5_url'])) { return $result['h5_url']; // 返回链接让用户点击跳转 } else { return $log ? $result : false; } } /** * [xcx 获取jssdk需要用到的数据] * @param [type] $order [订单信息数组] * @param boolean $log [description] * @param boolean $type [区分是否是小程序,默认 true] * @return [type] [description] */ public static function xcx($order=[], $log=false, $type=true) { if(empty($order['order_sn']) || empty($order['total_amount']) || empty($order['body']) || empty($order['openid'])){ die("订单数组信息缺失!"); } $order['type'] = 'jsapi'; // 获取订单类型,用户拼接请求地址 $config = self::$config; $result = self::unifiedorder($order, $type); if (!empty($result['prepay_id'])) { $data = array ( 'appid' => $type ? $config['xcxid'] : $config['appid'], // 由微信生成的应用id 'timestamp' => (string)time(), 'noncestr' => self::get_rand_str(32, 0, 1), // 随机32位字符串 'package' => 'prepay_id='.$result['prepay_id'], ); $data['paysign'] = self::makesign($data); $data['signtype'] = 'rsa'; return $data; // 数据小程序客户端 } else { return $log ? $result : false; } } /** * [scan 微信扫码支付] * @param [type] $order [订单信息数组] * @return [type] [description] */ public static function scan($order=[], $log=false) { if(empty($order['order_sn']) || empty($order['total_amount']) || empty($order['body'])){ die("订单数组信息缺失!"); } $order['type'] = 'native'; // native支付 $result = self::unifiedorder($order); if (!empty($result['code_url'])) { return urldecode($result['code_url']); // 返回链接让用户点击跳转 } else { return $log ? $result : false; } } /** * [notify 回调验证] * @return [array] [返回数组格式的notify数据] */ public static function notify($server = [], $response = []) { $config = self::$config; $server = $server $_server; $response = $response file_get_contents('php://input', 'r'); if (empty($response) || empty($server['http_wechatpay_signature'])) { return false; } $body = [ 'timestamp' => $server['http_wechatpay_timestamp'], 'nonce' => $server['http_wechatpay_nonce'], 'data' => $response, ]; // 验证应答签名 $verifysign = self::verifysign($body, trim($server['http_wechatpay_signature']), trim($server['http_wechatpay_serial'])); if (!$verifysign) { die("签名验证失败!"); } $result = json_decode($response, true); if (empty($result) || $result['event_type'] != 'transaction.success' || $result['summary'] != '支付成功') { return false; } // 加密信息 $associateddata = $result['resource']['associated_data']; $noncestr = $result['resource']['nonce']; $ciphertext = $result['resource']['ciphertext']; $data = $result['resource']['ciphertext'] = self::decrypttostring($associateddata, $noncestr, $ciphertext); return json_decode($data, true); } /** * [refund 微信支付退款] * @param [type] $order [订单信息] * @param [type] $type [是否是小程序] */ public static function refund($order) { $config = self::$config; if(empty($order['refund_sn']) || empty($order['refund_amount']) || (empty($order['order_sn']) && empty($order['transaction_id']))){ die("订单数组信息缺失!"); } $params = array( 'out_refund_no' => (string)$order['refund_sn'], // 商户退款单号 'funds_account' => 'available', // 退款资金来源 'amount' => [ 'refund' => $order['refund_amount'], 'currency' => 'cny', ] ); if (!empty($order['transaction_id'])) { $params['transaction_id'] = $order['transaction_id']; $orderdetail = self::query($order['transaction_id'], true); } else { $params['out_trade_no'] = $order['order_sn']; $orderdetail = self::query($order['order_sn']); } $params['amount']['total'] = $orderdetail['amount']['total']; !empty($order['reason']) && $params['reason'] = $order['reason']; $url = self::$refundurl; $header = self::createauthorization($url, $params, 'post'); $response = http::post($url, json_encode($params, json_unescaped_unicode), $header); $result = json_decode($response, true); return $result; } /** * [queryrefund 查询退款] * @param [type] $refundsn [退款单号] * @return [type] [description] */ public static function queryrefund($refundsn, $type = false) { $url = self::$refundurl . '/' . $refundsn; $params = ''; $header = self::createauthorization($url, $params, 'get'); $response = http::get($url, $params, $header); $result = json_decode($response, true); return $result; } /** * [success 通知支付状态] */ public static function success() { $str = ['code'=>'success', 'message'=>'成功']; die(json_encode($str, json_unescaped_unicode)); } /** * [createauthorization 获取接口授权header头信息] * @param [type] $url [请求地址] * @param array $data [请求参数] * @param string $method [请求方式] * @return [type] [description] */ //生成v3 authorization protected static function createauthorization($url, $data=[], $method='post'){ $config = self::$config; //商户号 $mchid = $config['mchid']; // 证书序列号 if (empty($config['serial_no'])) { $certfile = @file_get_contents($config['cert_client']); $certarr = openssl_x509_parse($publicstr); $serial_no = $certarr['serialnumberhex']; } else { $serial_no = $config['serial_no']; } // 解析url地址 $url_parts = parse_url($url); //生成签名 $body = [ 'method' => $method, 'url' => ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : "")), 'time' => time(), // 当前时间戳 'nonce' => self::get_rand_str(32, 0, 1), // 随机32位字符串 'data' => (strtolower($method) == 'post' ? json_encode($data, json_unescaped_unicode) : $data), // post请求时 需要 转json字符串 ]; $sign = self::makesign($body); //authorization 类型 $schema = 'wechatpay2-sha256-rsa2048'; //生成token $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $mchid, $body['nonce'], $body['time'], $serial_no, $sign); $header = [ 'content-type:application/json', 'accept:application/json', 'user-agent:*/*', 'authorization: '. $schema . ' ' . $token ]; return $header; } /** * [makesign 生成签名] * @param [type] $data [加密数据] * @return [type] [description] */ public static function makesign($data) { $config = self::$config; if (!in_array('sha256withrsaencryption', \openssl_get_md_methods(true))) { throw new \runtimeexception("当前php环境不支持sha256withrsa"); } // 拼接生成签名所需的字符串 $message = ''; foreach ($data as $value) { $message .= $value . "\n"; } // 商户私钥 $private_key = self::getprivatekey($config['cert_key']); // 生成签名 openssl_sign($message, $sign, $private_key, 'sha256withrsaencryption'); $sign = base64_encode($sign); return $sign; } /** * [verifysign 验证签名] * @param [type] $data [description] * @param [type] $sign [description] * @param [type] $serial [description] * @return [type] [description] */ public static function verifysign($data, $sign, $serial) { $config = self::$config; if (!in_array('sha256withrsaencryption', \openssl_get_md_methods(true))) { throw new \runtimeexception("当前php环境不支持sha256withrsa"); } $sign = \base64_decode($sign); // 拼接生成签名所需的字符串 $message = ''; foreach ($data as $value) { $message .= $value . "\n"; } // 获取证书相关信息 self::certificates($serial); // 平台公钥 $public_key = self::getpublickey($config['public_key']); //平台公钥 // 验证签名 $recode = \openssl_verify($message, $sign, $public_key, 'sha256withrsaencryption'); return $recode == 1 ? true : false; } //获取私钥 public static function getprivatekey($filepath) { return openssl_pkey_get_private(file_get_contents($filepath)); } //获取公钥 public static function getpublickey($filepath) { return openssl_pkey_get_public(file_get_contents($filepath)); } /** * [certificates 获取证书] * @return [type] [description] */ public static function certificates($serial) { $config = self::$config; $publicstr = @file_get_contents($config['public_key']); if ($publicstr) { // 判断证书是否存在 $openssl = openssl_x509_parse($publicstr); if ($openssl['serialnumberhex'] == $serial) { // 是否是所需证书 // return self::getpublickey($config['public_key']); //平台公钥 return ''; } } $url = self::$certificatesurl; $params = ''; $header = self::createauthorization($url, $params, 'get'); $response = http::get($url, $params, $header); $result = json_decode($response, true); if (empty($result['data'])) { throw new runtimeexception("[" . $result['code'] . "] " . $result['message']); } foreach ($result['data'] as $key => $certificate) { if ($certificate['serial_no'] == $serial) { $publickey = self::decrypttostring( $certificate['encrypt_certificate']['associated_data'], $certificate['encrypt_certificate']['nonce'], $certificate['encrypt_certificate']['ciphertext'] ); file_put_contents($config['public_key'], $publickey); break; // 终止循环 } // self::$publickey[$certificate['serial_no']] = $publickey; } // return self::getpublickey($config['public_key']); //平台公钥 } /** * [decrypttostring 证书和回调报文解密] * @param [type] $associateddata [附加数据包(可能为空)] * @param [type] $noncestr [加密使用的随机串初始化向量] * @param [type] $ciphertext [base64编码后的密文] * @return [type] [description] */ public static function decrypttostring($associateddata, $noncestr, $ciphertext) { $config = self::$config; $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, $config['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, $config['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', $config['key'], \openssl_raw_data, $noncestr, $authtag, $associateddata); } throw new \runtimeexception('aead_aes_256_gcm需要php 7.1以上或者安装libsodium-php'); } /** fengkui.net * [get_rand_str 获取随机字符串] * @param integer $randlength [长度] * @param integer $addtime [是否加入当前时间戳] * @param integer $includenumber [是否包含数字] * @return [type] [description] */ public static function get_rand_str($randlength=6, $addtime=0, $includenumber=1) { if ($includenumber) $chars='abcdefghijklmnopqrstuvwxyzabcdefghjklmnpqest123456789'; $chars='abcdefghijklmnopqrstuvwxyz'; $len = strlen($chars); $randstr = ''; for ($i=0; $i<$randlength; $i++){ $randstr .= $chars[rand(0, $len-1)]; } $tokenvalue = $randstr; $addtime && $tokenvalue = $randstr . time(); return $tokenvalue; } /** fengkui.net * [get_ip 定义一个函数get_ip() 客户端ip] * @return [type] [description] */ public static function get_ip() { if (getenv("http_client_ip")) $ip = getenv("http_client_ip"); else if(getenv("http_x_forwarded_for")) $ip = getenv("http_x_forwarded_for"); else if(getenv("remote_addr")) $ip = getenv("remote_addr"); else $ip = "unknow"; if(preg_match('/^((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1 -9]?\d))))$/', $ip)) return $ip; else return ''; }}
本文参考文档:
1、微信支付 小程序 (v3)- php 完整后端代码
2、php 微信小程序 微信支付 v3
3、微信支付v3版本小程序支付 php签名,验签,数据解密代码分享(完整方法主参考)
4、微信支付 api v3 回调通知 签名验证 phpdemo有嘛?
推荐:《php视频教程》以上就是随着微信支付的升级,php微信支付类v3接口也来了的详细内容。
其它类似信息

推荐信息