tp5.1 微信支付、支付宝、招商支付(Payment)
插件合集
源地址
https://github.com/helei112g/payment
安装
当前 Payment
项目仅支持 PHP version > 7.0
的版本,并且仅支持通过 composer
进行安装。
需要 PHP
安装以下扩展:
- ext-curl - ext-mbstring - ext-bcmath - package-Guzzle
Composer安装
composer require "riverslei/payment:*"
composer update
直接上代码
<?php /** * Created by PhpStorm. * User: Zhangyongfeng * Date: 2020/11/24 * Time: 11:27 * * ━━━━━━━━━神兽出没━━━━━━━━━ * * ┏┓ ┏┓+ + * ┏┛┻━━━┛┻┓ + + * ┃ ┃ * ┃ ━ ┃ ++ + + + * ████━████ ┃+ * ┃ ┃ + * ┃ ┻ ┃ * ┃ ┃ + + * ┗━┓ ┏━┛ * ┃ ┃ * ┃ ┃ + + + + * ┃ ┃ Code is far away from bug with the animal protecting * ┃ ┃ + 神兽保佑,代码无bug * ┃ ┃ * ┃ ┃ + * ┃ ┗━━━┓ + + * ┃ ┣┓ * ┃ ┏┛ * ┗┓┓┏━┳┓┏┛ + + + + * ┃┫┫ ┃┫┫ * ┗┻┛ ┗┻┛+ + + + * * ━━━━━━━━━感觉萌萌哒━━━━━━━━━ */ namespace zyf; // 博客地址 https://www.cnblogs.com/zyfeng/p/14034536.html class Pay { protected $wxConfig = []; protected $aliConfig = []; public static function returnCode($code = '', $msg = '', $data = []) { return \app\common\controller\Base::returnData($code, $msg, $data); } /** * 构造函数 * @access protected * @param array $options 参数 * @author zyf <1322816443@qq.com> */ public function __construct($options = []) { foreach ($options as $name => $item) { if (property_exists($this, $name)) { $this->$name = $item; } } $this->wxConfig = [ 'debug' => config("pay.wechat.debug"), 'app_id' => config("pay.wechat.app_id"), 'mch_id' => config("pay.wechat.mch_id"), 'md5_key' => config("pay.wechat.md5_key"), 'app_cert_pem' => dirname(__FILE__). DIRECTORY_SEPARATOR . 'pem' . DIRECTORY_SEPARATOR . 'apiclient_cert.pem', 'app_key_pem' => dirname(__FILE__). DIRECTORY_SEPARATOR . 'pem' . DIRECTORY_SEPARATOR . 'apiclient_key.pem', 'sign_type' => 'MD5', // MD5 HMAC-SHA256 'limit_pay' => [], // 指定不能使用信用卡支付 不传入,则均可使用 'fee_type' => 'CNY', // 货币类型 当前仅支持该字段 'notify_url' => $_SERVER['REQUEST_SCHEME'] . "://". $_SERVER['SERVER_NAME'] ."/common/Pay/wxNotify", 'redirect_url' => 'https://dayutalk.cn/' ]; $this->aliConfig = [ 'use_sandbox' => config("pay.alipay.debug"), 'app_id' => config("pay.alipay.app_id"), 'sign_type' => 'RSA2', // RSA RSA2 'ali_public_key' => config("pay.alipay.public_key"), 'rsa_private_key' => config("pay.alipay.private_key"), 'limit_pay' => config("pay.alipay.limit_pay"), 'notify_url' => $_SERVER['REQUEST_SCHEME'] . "://". $_SERVER['SERVER_NAME'] ."/common/Pay/aliNotify", 'return_url' => $_SERVER['REQUEST_SCHEME'] . "://". $_SERVER['SERVER_NAME'] ."/common/Pay/aliNotify", 'fee_type' => 'CNY', // 货币类型 当前仅支持该字段 ]; } /** * [order_num 生成订单号] * @param string $prefix 订单前缀(如:JD-) * @return string 输出订单号字符串 */ public function order_num($prefix = '') { $micro = substr(microtime(), 2, 3); return $prefix . date("YmdHis") . $micro . rand(100000, 999999); } /** * 微信支付 * @param string $type 'app' => APP * @param string $type 'pub' => 公众号 * @param string $type 'qr' => 扫码支付(可以使用app的帐号,也可以用公众的帐号完成) * @param string $type 'single' => 单次分账 * @param string $type 'bar' => 刷卡支付,与支付宝的条码支付对应 * @param string $type 'lite' => 小程序 * @param string $type 'wap' => wap支付,针对特定用户 * @param string $type 'multi' => 多次分账 * @param array $data['body'] 商品描述 * @param array $data['subject'] 商品的标题 * @param array $data['trade_no'] 商户网站唯一订单号 * @param array $data['time_expire'] 该笔订单允许的最晚付款时间,逾期将关闭交易 * @param array $data['amount'] 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] * @param array $data['return_param'] 公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。 * @param array $data['client_ip'] 必须传正确的用户端IP,支持ipv4、ipv6格式,获取方式详见 * @param array $data['openid'] $type=pub此参数必传,用户在商户appid下的唯一标识 * @param array $data['product_id'] $type=qr此参数必传,此id为二维码中包含的商品ID,商户自行定义 * @return string|array * @author zyf <1322816443@qq.com> * */ public function wxPay($type = "app", $data = []) { // 请求参数,完整参数见具体表格 $payData = [ 'body' => 'test body', 'subject' => 'test subject', 'trade_no' => '', // 自己实现生成 'time_expire' => time() + 600, // 表示必须 600s 内付款 'amount' => '5.20', // 微信沙箱模式,需要金额固定为3.01 'return_param' => '123', 'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1', // 客户地址 'openid' => '', 'product_id' => '', 'attach' => '自定义数据' ]; if ($type == 'pub') { if (empty($data['openid'])) { return self::returnCode(999, '公众号支付请传openid'); } } if ($type == 'qr') { if (empty($data['product_id'])) { return self::returnCode(999, '扫码支付请传product_id'); } } if ($data) { foreach ($data as $k => $v) { if (isset($payData[$k])) { $payData[$k] = $v; } } } // 使用 try { $client = new \Payment\Client(\Payment\Client::WECHAT, $this->wxConfig); switch ($type) { case "app": $res = $client->pay(\Payment\Client::WX_CHANNEL_APP, $payData); break; case "pub": $res = $client->pay(\Payment\Client::WX_CHANNEL_PUB, $payData); break; case "qr": $res = $client->pay(\Payment\Client::WX_CHANNEL_QR, $payData); break; case "single": $res = $client->pay(\Payment\Client::WX_SETTLE_SINGLE, $payData); break; case "bar": $res = $client->pay(\Payment\Client::WX_CHANNEL_BAR, $payData); break; case "lite": $res = $client->pay(\Payment\Client::WX_CHANNEL_LITE, $payData); break; case "wap": $res = $client->pay(\Payment\Client::WX_CHANNEL_WAP, $payData); break; case "multi": $res = $client->pay(\Payment\Client::WX_SETTLE_MULTI, $payData); break; } return $res; } catch (\InvalidArgumentException $e) { echo $e->getMessage(); exit; } catch (\Payment\Exceptions\GatewayException $e) { echo $e->getMessage(); var_dump($e->getRaw()); exit; } catch (\Payment\Exceptions\ClassNotFoundException $e) { echo $e->getMessage(); exit; } catch (\Exception $e) { echo $e->getMessage(); exit; } } /** * 查看订单支付状态 判断trade_state==SUCCESS 代表支付成功 * @param number trade_no 业务生成的订单号 * @param number transaction_id 交易编号 * @return array * @author zyf <1322816443@qq.com> * */ public function wxTradeQuery($data) { // 使用 try { $client = new \Payment\Client(\Payment\Client::WECHAT, $this->wxConfig); $res = $client->tradeQuery($data); return $res; } catch (InvalidArgumentException $e) { echo $e->getMessage(); exit; } catch (\Payment\Exceptions\GatewayException $e) { echo $e->getMessage(); exit; } catch (\Payment\Exceptions\ClassNotFoundException $e) { echo $e->getMessage(); exit; } } /** * 微信签名 jsapi页面用到 * @param string appid 用户appid * @param string nonceStr 支付生成的随机串 * @param string package 'prepay_id='.$data['PREPAY_ID'] 支付生成的标识编码 * @param string signType 加密方式 线上:MD5 沙箱。。。 * @param string timeStamp 时间戳 * @return string * @author zyf <1322816443@qq.com> */ public function wxMakeSign($data) { $stringA = ''; foreach ($data as $key => $value) { if (!$value) { continue; } if ($stringA) { $stringA .= '&'.$key."=".$value; } else { $stringA = $key."=".$value; } } $wx_key = $this->wxConfig['md5_key']; $stringSignTemp = $stringA.'&key='.$wx_key; return strtoupper(md5($stringSignTemp)); } /* * 微信支付回调 * */ public function wxNotify() { // 实例化继承了接口的类 $callback = new PayNotify(); try { $client = new \Payment\Client(\Payment\Client::WECHAT, $this->wxConfig); $xml = $client->notify($callback); //写业务逻辑 } catch (\InvalidArgumentException $e) { echo $e->getMessage(); exit; } catch (\Payment\Exceptions\GatewayException $e) { echo $e->getMessage(); exit; } catch (\Payment\Exceptions\ClassNotFoundException $e) { echo $e->getMessage(); exit; } catch (\Exception $e) { echo $e->getMessage(); exit; } } /** * 支付宝支付 * @param string $type 'app' => 手机app * @param string $type 'wap' => 手机网页 * @param string $type 'web' => PC网页 * @param string $type 'qr' => 扫码支付 * @param string $type 'bar' => 条码支付 * @param array $data['body'] 商品描述 * @param array $data['subject'] 商品的标题 * @param array $data['trade_no'] 商户网站唯一订单号 * @param array $data['time_expire'] 该笔订单允许的最晚付款时间,逾期将关闭交易 * @param array $data['amount'] 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] * @param array $data['return_param'] 公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。 * @param array $data['goods_type'] 商品主类型 :0-虚拟类商品,1-实物类商品 * @param array $data['store_id'] 商户门店编号 * @return string|array * @author zyf <1322816443@qq.com> * */ public function aliPay($type = "app", $data = []) { $payData = [ 'body' => 'ali qr pay', 'subject' => '测试支付宝APP支付', 'trade_no' => time() . rand(1000, 9999), 'time_expire' => time() + 600, // 表示必须 600s 内付款 'amount' => '0.01', // 单位为元 ,最小为0.01 'return_param' => '{"code":"111","type":"'. $type .'"}', 'goods_type' => '1', // 0—虚拟类商品,1—实物类商品 'store_id' => '', ]; if ($data) { foreach ($data as $k => $v) { if ($payData[$k]) { $payData[$k] = $v; } } } // 使用 try { $client = new \Payment\Client(\Payment\Client::ALIPAY, $this->aliConfig); switch ($type) { case "web": $res = $client->pay(\Payment\Client::ALI_CHANNEL_WEB, $payData); break; case "app": $res = $client->pay(\Payment\Client::ALI_CHANNEL_APP, $payData); break; case "wap": $res = $client->pay(\Payment\Client::ALI_CHANNEL_WAP, $payData); break; case "qr": $res = $client->pay(\Payment\Client::ALI_CHANNEL_QR, $payData); break; case "bar": $res = $client->pay(\Payment\Client::ALI_CHANNEL_BAR, $payData); break; } return $res; } catch (\InvalidArgumentException $e) { echo $e->getMessage(); exit; } catch (\Payment\Exceptions\GatewayException $e) { echo $e->getMessage(); var_dump($e->getRaw()); exit; } catch (\Payment\Exceptions\ClassNotFoundException $e) { echo $e->getMessage(); exit; } catch (\Exception $e) { echo $e->getMessage(); exit; } } /* * 支付宝支付回调 * { * "channel":"Alipay", 通知的渠道,如:支付宝、微信、招商 * "type":"pay", 通知的类型,如:支付、退款 * "way":"sync", 通知的方式,如:异步 async,同步 sync * "data":{ * "charset":"utf-8", 编码 * "out_trade_no":"16069883507794", 数据库中的订单号 * "method":"alipay.trade.page.pay.return", * "total_amount":"0.01", 金额 * "trade_no":"2020120322001440350504855181", 支付宝生成的订单号 * "auth_app_id":"2016073100130857", 用户appid * "version":"1.0", * "app_id":"2016073100130857", * "seller_id":"2088102169252684", * "timestamp":"2020-12-03 17:39:42" 时间 * } * } * @author zyf <1322816443@qq.com> * */ public function aliNotify() { // 实例化继承了接口的类 $callback = new PayNotify(); $file = $_SERVER['DOCUMENT_ROOT'].'/callback.text'; try { $client = new \Payment\Client(\Payment\Client::ALIPAY, $this->aliConfig); $xml = $client->notify($callback); // 写业务逻辑 $request = json_decode((string)$xml, true); $fp = fopen($file, 'a'); fwrite($fp, json_encode($xml)); fclose($fp); } catch (\InvalidArgumentException $e) { echo $e->getMessage(); exit; } catch (\Payment\Exceptions\GatewayException $e) { echo $e->getMessage(); exit; } catch (\Payment\Exceptions\ClassNotFoundException $e) { echo $e->getMessage(); exit; } catch (\Exception $e) { echo $e->getMessage(); exit; } } }
微信JSAPI
页面
{include file="public/header" /} </head> <body> <font color="#9ACD32"> <b>该笔订单支付金额为<span style="color:#f00;font-size:50px">1分</span>钱</b> </font><br/><br/> <div align="center"> <button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" type="button" onclick="callpay()" >立即支付</button> </div> </body> </html> <script> //调用微信JS api 支付 function jsApiCall() { WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId": "{$data.appid}", "nonceStr": "{$data.nonce_str}", "package": "{$data.prepay_id}", "paySign": "{$data.sign}", "signType": "MD5", "timeStamp": "{$data.timeStamp}", }, function(res){ console.log(res) if(res.err_msg=='get_brand_wcpay_request:ok'){ window.location.href="{:url('resulte')}"; }else if(res.err_msg=='get_brand_wcpay_request:cancel'){ alert('未支付'); }else if(res.err_msg=='get_brand_wcpay_request:fail'){ alert('支付失败'); }else{ console.log(res) } } ); } function callpay() { if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', jsApiCall, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', jsApiCall); document.attachEvent('onWeixinJSBridgeReady', jsApiCall); } }else{ jsApiCall(); } } </script>
后台
/** * 微信jsapi支付 公众号 * @param string amount 金额 * @param string trade_no 生成的唯一订单号 * */ public function wxJsapi() { $payMod = new Pay(); $payData = [ 'trade_no' => '', 'amount' => '' ]; $res = $payMod->wxPay('pub', $payData); $data['appid'] = ''; $data['nonceStr'] = $res['nonce_str']; $data['package'] = 'prepay_id='. $res['prepay_id']; $data['singType'] = 'MD5'; $data['timeStamp'] = time(); $data['sign'] = $payMod->wxMakeSign($data); $this->render('', $data); }
微信H5支付
/** * 微信H5支付 wap * @param string amount 金额 * @param string trade_no 生成的唯一订单号 * @param string scene_info 场景值 * */ public function wxWap() { $payMod = new Pay(); $payData = [ 'amount' => '', 'trade_no' => '', 'scene_info' => [ 'type' => 'Wap', // IOS Android Wap 腾讯建议 IOS ANDROID 采用app支付 'wap_url' => 'http://', // 自己的 wap 地址 'wap_name' => '网站名称', ] ]; // 跳转下方返回链接即可 return $payMod->wxPay('wap', $payData)['mweb_url']; }
微信扫码支付
需要用到二维码生成,可使用本人博客:
https://www.cnblogs.com/zyfeng/p/14143957.html
后台
/** * 微信native支付 扫码支付 * @param string amount 金额 * @param string product_id 产品id * @param string trade_no 生成的唯一订单号 * @return mixed */ public function wxNative() { $payMod = new Pay(); $qrCode = new QRcode(); $payData = [ 'amount' => '', 'product_id' => '', 'trade_no' => '', ]; $data = $payMod->wxPay('qr', $payData); $qrCode->png($data['code_url']); return $this->render('', [ 'img' => $qrCode->show() ]); } /** * 微信支付确认订单状态 * @param string trade_no 生成的唯一订单号 * */ public function isOrderType() { $payMod = new Pay(); $payData = [ 'trade_no' => '', ]; $res = $payMod->wxTradeQuery($payData); if ($res['trade_state'] == 'SUCCESS') { // 修改订单状态 } // 写业务逻辑 }
页面
<html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1" /> <script src="/static/index/js/jquery.min.js" type="text/javascript" charset="utf-8"></script> <title>微信支付</title> </head> <body> <div style="text-align: center;"> <div style="margin-left: 10px;color:#556B2F;font-size:30px;font-weight: bolder;">扫描支付</div><br/> {$img | raw} <!-- <div style="color:#ff0000"><b>微信支付样例程序,仅做参考</b></div>--> <div id="myDiv"></div> <!-- <div id="timer">0</div>--> </div> <!--<img alt="模式二扫码支付" src="" style="width:150px;height:150px;"/>--> <script> //设置每隔1000毫秒执行一次load() 方法 var myIntval = setInterval(function(){load()},1000); function load(){ // document.getElementById("timer").innerHTML=parseInt(document.getElementById("timer").innerHTML)+1; var xmlhttp; if (window.XMLHttpRequest){ // code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp = new XMLHttpRequest(); }else{ // code for IE6, IE5 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.onreadystatechange = function(){ if (xmlhttp.readyState == 4 && xmlhttp.status==200){ trade_state=xmlhttp.responseText; if(trade_state == 'SUCCESS'){ document.getElementById("myDiv").innerHTML = '支付成功'; //alert(transaction_id); //延迟3000毫秒执行tz() 方法 clearInterval(myIntval); }else if(trade_state == 'REFUND'){ document.getElementById("myDiv").innerHTML = '转入退款'; clearInterval(myIntval); }else if(trade_state == 'NOTPAY'){ document.getElementById("myDiv").innerHTML = '请扫码支付'; }else if(trade_state == 'CLOSED'){ document.getElementById("myDiv").innerHTML = '已关闭'; clearInterval(myIntval); }else if(trade_state == 'REVOKED'){ document.getElementById("myDiv").innerHTML = '已撤销'; clearInterval(myIntval); }else if(trade_state == 'USERPAYING'){ document.getElementById("myDiv").innerHTML = '用户支付中'; }else if(trade_state == 'PAYERROR'){ document.getElementById("myDiv").innerHTML = '支付失败'; clearInterval(myIntval); } } } //orderquery.php 文件返回订单状态,通过订单状态确定支付状态 xmlhttp.open("POST", "{:url('isOrderType')}", false); //下面这句话必须有 //把标签/值对添加到要发送的头文件。 xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xmlhttp.send("out_trade_no=1"); } </script> </body> </html>