支付是基于ThinkCMF的电商版里面的支付插件 pay 做的。
电商版地址:https://github.com/thinkcmf/mall
插件可以在里面的 public/plugins/pay
1.注册微信公众号,获取里面的APPID,APPSECRET
2.配置授权域名,白名单等
3.微信商户号申请Native支付,JSAPI支付,h5支付(有的默认是就有的)
4.获取商户号(10位的数字)配置商户秘钥(KEY)
5.配置支付授权目录
JSAPI的授权目录 http://www.xxxxx.com/order/payment/
Native回调地址 http://www.xxxxx.com/plugin/pay/wechat/notify.html
【以上地址是根据ThinkCMF电商版的商城模块来的】
1.注册支付宝商户平台
2.注册支付宝开发者平台
3.获取支付宝的公钥私钥,这个支付宝提供了一个生成的工具,下载下来生成一下就可以
https://opendocs.alipay.com/open/291/106097/
4.获取支付宝APPID,应用私钥,支付宝公钥(注意这里会有一个应用公钥,就是生成的公钥,不是那个)
这套操作下来,电商版的支付就可以成功了。但是我在使用的时候发现了一些BUG。
BUG 1:
插件里面的pay/lib/Config.php 微信回调地址错误
public static function get($key = false)
{
$data = [];
$config = cmf_get_plugin_config('Pay');
$dev = isset($config['dev']) ? $config['dev'] : false;
$config = [
// 微信支付参数
'wechat' => [
'debug' => $dev, // 沙箱模式
'app_id' => isset($config['wx_app_id']) ? $config['wx_app_id'] : '', // 应用ID
'mch_id' => isset($config['wx_mch_id']) ? $config['wx_mch_id'] : '', // 微信支付商户号
'mch_key' => isset($config['wx_mch_key']) ? $config['wx_mch_key'] : '', // 微信支付密钥
'ssl_cer' => '', // 微信证书 cert 文件
'ssl_key' => '', // 微信证书 key 文件
'notify_url' => cmf_plugin_url('Pay://Wechat/notify', [], true), // 支付通知URL
'cache_path' => Env::get('runtime_path'),// 缓存目录配置(沙箱模式需要用到)
],
// 支付宝支付参数
'alipay' => [
'debug' => $dev, // 沙箱模式
'app_id' => isset($config['ali_app_id']) ? $config['ali_app_id'] : '', // 应用ID
'public_key' => isset($config['ali_public_key']) ? $config['ali_public_key'] : '',
'private_key' => isset($config['ali_private_key']) ? $config['ali_private_key'] : '',
'notify_url' => cmf_plugin_url('Pay://Alipay/notify', [], true), // 支付通知URL
'return_url' => cmf_url('order/order/index',[],true,true),
]
];
if($key && isset($config[$key])){
return $config[$key];
}
return $config;
}
BUG 2:
pay/controller/WechatController 里面初始化配置获取错误
public function initialize()
{
$this->config = \plugins\pay\lib\Config::get();
}
BUG3:
pay/PayPlugin.php 里面的安装文字不对,统一下单里面公众号支付缺少openid
namespace plugins\pay;
use cmf\lib\Plugin;
use think\Db;
use think\exception\HttpResponseException;
class PayPlugin extends Plugin
{
const CMF_WECHAT_QRCODE = 'cmf-wechat-qrcode';
const CMF_WECHAT_H5 = 'cmf-wechat-h5';
const CMF_WECHAT_MP = 'cmf-wechat-mp';
const CMF_WECHAT_MINI = 'cmf-wechat-miniapp'; //增加微信小程序支付
const CMF_ALIPAY_WEB = 'cmf-alipay-web';
const CMF_ALIPAY_H5 = 'cmf-alipay-h5';
public $info = [
'name' => 'Pay',
'title' => '支付插件',
'description' => '支付插件',
'status' => 1,
'author' => '五五',
'version' => '1.0',
'demo_url' => 'http://www.thinkcmf.com',
'author_url' => 'http://www.thinkcmf.com'
];
public $hasAdmin = 0;
// 插件安装
public function install()
{
Db::name('OrderPayment')->delete(true);
Db::name('OrderPayment')->insertAll([
[
'code' => self::CMF_WECHAT_QRCODE,
'name' => '微信扫码支付',
'description' => '微信扫码支付',
'tips' => '微信扫码',
],
[
'code' => self::CMF_WECHAT_H5,
'name' => '微信H5支付',
'description' => '微信H5支付',
'tips' => '微信H5',
],
[
'code' => self::CMF_WECHAT_MP,
'name' => '微信支付',
'description' => '微信支付',
'tips' => '微信',
],
[
'code' => self::CMF_WECHAT_MINI,
'name' => '微信小程序支付',
'description' => '微信小程序支付',
'tips' => '微信小程序',
],
[
'code' => self::CMF_ALIPAY_WEB,
'name' => '支付宝web支付',
'description' => '支付宝web支付',
'tips' => '支付宝跳转',
],
[
'code' => self::CMF_ALIPAY_H5,
'name' => '支付宝H5支付',
'description' => '支付宝H5支付',
'tips' => '支付宝跳转',
]
]);
return true;//安装成功返回true,失败false
}
// 插件卸载
public function uninstall()
{
return true;//卸载成功返回true,失败false
}
/**
* 统一下单
*
* @param array $params
* @return bool|mixed|\think\Response
*/
public function orderPaymentUnifiedorder($params = [])
{
try {
switch ($params['code']) {
case self::CMF_WECHAT_QRCODE:
$payType = 'wechat';
$payMethod = 'scan';
$order = [
'out_trade_no' => $params['sn'],
'body' => $params['sn'],
'total_fee' => (string)bcmul($params['amount'], 100),
];
break;
case self::CMF_WECHAT_MP:
$payType = 'wechat';
$payMethod = 'mp';
$order = [
'out_trade_no' => $params['sn'],
'body' => $params['sn'],
'openid' => $params['openid'],
'total_fee' => (string)bcmul($params['amount'], 100),
];
break;
case self::CMF_WECHAT_H5:
$payType = 'wechat';
$payMethod = 'wap';
$order = [
'out_trade_no' => $params['sn'],
'body' => $params['sn'],
'total_fee' => (string)bcmul($params['amount'], 100),
];
break;
case self::CMF_WECHAT_MINI:
$payType = 'wechat';
$payMethod = 'miniapp';
$order = [
'out_trade_no' => $params['sn'],
'body' => $params['body'],
'attach' => $params['attach'],
'openid' => $params['openid'],
'total_fee' => (string)bcmul($params['amount'], 100),
];
break;
case self::CMF_ALIPAY_WEB:
$payType = 'alipay';
$payMethod = 'web';
$order = [
'out_trade_no' => $params['sn'],
'subject' => $params['sn'],
'total_amount' => $params['amount'],
];
break;
case self::CMF_ALIPAY_H5:
$payType = 'alipay';
$payMethod = 'wap';
$order = [
'out_trade_no' => $params['sn'],
'subject' => $params['sn'],
'total_amount' => $params['amount'],
];
break;
default:
return false;
}
$config = \plugins\pay\lib\Config::get();
$pay = new \Pay\Pay($config);
$options = $pay->driver($payType)->gateway($payMethod)->apply($order);
switch ($params['code']) {
case self::CMF_WECHAT_QRCODE:
return $options;
break;
case self::CMF_WECHAT_MP:
return $options;
break;
case self::CMF_WECHAT_H5:
throw new HttpResponseException(\redirect($options));
break;
case self::CMF_WECHAT_MINI:
return $options;
break;
case self::CMF_ALIPAY_WEB:
throw new HttpResponseException(\response($options));
break;
case self::CMF_ALIPAY_H5:
throw new HttpResponseException(\response($options));
break;
default:
return $options;
}
} catch (Exception $e) {
return false;
}
}
/**
* 支付渠道
*
* @param array $params
* @return array|\PDOStatement|string|\think\Collection
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function orderPayment($params = [])
{
$data = [];
if (cmf_is_mobile()) {
$data = Db::name('OrderPayment')
->where('status', 1)
->where('code',self::CMF_ALIPAY_H5)
->whereOr(
'code',self::CMF_WECHAT_H5
)
->field(true)
->select();
} else {
$data = Db::name('OrderPayment')
->where('status', 1)
->where('code',self::CMF_ALIPAY_WEB)
->whereOr(
'code',self::CMF_WECHAT_QRCODE
)
->field(true)
->select();
}
if (cmf_is_wechat()) {
$data = Db::name('OrderPayment')
->where('status', 1)
->where('code',self::CMF_WECHAT_MP)
->field(true)
->select();
}
if (strpos($_SERVER['HTTP_USER_AGENT'], 'AlipayClient') !== false) {
$data = Db::name('OrderPayment')
->where('status', 1)
->where('code',self::CMF_ALIPAY_H5)
->field(true)
->select();
}
return $data;
}
}
PaymentController 里面修改,加上获取openid,加上扫码支付时,判断支付状态
// +----------------------------------------------------------------------
// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 老猫
// +----------------------------------------------------------------------
namespace app\order\Controller;
use app\order\model\OrderModel;
use cmf\controller\UserBaseController;
use think\Db;
class PaymentController extends UserBaseController
{
public function getCode()
{
$paymentId = $this->request->param('payment_id', 0, 'intval');
$paymentCode = $this->request->param('payment_code', '');
$orderId = $this->request->param('order_id');
if (empty($orderId)) {
$this->error('订单不能为空');
}
$userId = cmf_get_current_user_id();
if (empty($paymentId)) {
$payment = Db::name("OrderPayment")->where('code', $paymentCode)->find();
} else {
$payment = Db::name("OrderPayment")->where('id', $paymentId)->find();
}
$findOrder = Db::name('Order')->where(['user_id' => $userId, 'id' => $orderId])->find();
if (empty($findOrder)) {
$this->error('订单不存在!');
} else if ($findOrder['pay_status'] == 1) {
$this->error('订单已支付!');
}
$userPayments = session('user_payments');
$userPayments = empty($userPayments) ? [] : $userPayments;
if (!in_array($payment['code'], $userPayments) && $findOrder['payment_code'] != $payment['code']) {
$this->error('非法支付方式!');
}
$data = ['payment_code' => $payment['code'], 'payment_name' => $payment['name']];
if (empty($payment['is_prepay']) && $findOrder['pay_status'] != 10) {
$data['pay_status'] = 10;//10:等待支付
}
//修改订单支付方式
Db::name('Order')->where(['user_id' => $userId, 'id' => $orderId, 'pay_status' => 0])->update($data);
$order = Db::name('Order')->where(['user_id' => $userId, 'id' => $orderId])->find();
$params = [
'code' => $payment['code'],
'amount' => $order['order_amount'],
'sn' => $order['order_sn']
];
//修改的,这里是微信JSAPI支付要带上openid
$openid = session('openid');
if($payment['code'] == 'cmf-wechat-mp'){
$params['openid'] = $openid;
}
$info = hook_one('order_payment_unifiedorder', $params);
if(cmf_is_wechat()){
$info = json_encode($info);
}
$this->assign([
'order_sn' => $order['order_sn'],
'payment'=>$payment,
'info'=>$info
]);
return $this->fetch('payment');
}
/**
* 判断订单状态
*/
public function check(){
$order_sn = $this->request->param('sn');
$order = OrderModel::where('order_sn',$order_sn)->find();
if($order['pay_status'] == 1){
$this->success('success');
}else{
$this->error('error');
}
}
}
JSAPI支付页面
<extend name="public@base"/>
<block name="seo">
<title>填写核对订单</title>
<meta name="keywords" content=""/>
<meta name="description" content=""/>
</block>
<block name="main">
</block>
<block name="footer"></block>
<block name="script">
<script type="text/javascript">
var url = "{:url('order/Order/index')}";
//调用微信JS api 支付
function jsApiCall()
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',{$info},
function(res){
WeixinJSBridge.log(res.err_msg);
if(res.err_msg == "get_brand_wcpay_request:ok"){
window.location.href= url;
}else if(res.err_msg == "get_brand_wcpay_request:cancel"){
alert('取消支付');
window.location.href = document.referrer;
}else if(res.err_msg == "get_brand_wcpay_request:fail"){
alert('支付失败');
}
}
);
}
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();
}
}
callpay();
</script>
</block>
微信扫码支付页面
<extend name="public@base"/>
<block name="seo">
<title>填写核对订单</title>
<meta name="keywords" content=""/>
<meta name="description" content=""/>
</block>
<block name="css">
<style>
.table-with-head thead {
background: #eee;
}
.table-with-head {
border: 1px solid #ddd;
}
blockquote {
margin-bottom: 0;
}
</style>
</block>
<block name="main">
<div class="container">
<div class="row">
<div class="row"></div>
<div class="row">
<div class="col-md-12 col-xs-12 col-sm-12">
</div>
<div class="col-md-4 col-xs-4 col-sm-4">
<h1 style="text-align: center">微信支付</h1>
<div id="qrcode" data-url="{$info}"></div>
</div>
<div class="col-md-4 col-xs-4 col-sm-4"></div>
</div>
</div>
</div>
</block>
<block name="footer"></block>
<block name="script">
<style>
#qrcode > img {
width: 100%;
}
</style>
<script>
Wind.use('__TMPL__/public/assets/js/qrcode.min.js', function () {
var url = $('#qrcode').data('url');
new QRCode(document.getElementById("qrcode"),url);
});
var order_sn = {$order_sn};
setInterval(function(){
$.get("{:url('order/payment/check')}",{sn:order_sn},function(res){
console.log(res);
})
},2000);
</script>
</block>
openid获取方式,没做微信登陆
在HomeBaseController 里面增加判断
protected function initialize()
{
// 监听home_init
hook('home_init');
parent::initialize();
if(cmf_is_wechat() && !session('openid')){
$this->getOpenid();
}
$siteInfo = cmf_get_site_info();
View::share('site_info', $siteInfo);
}
protected function getOpenid(){
$appid = 'appid';
$appkey = 'appkey';
$redirect_uri = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
if (!isset($_GET['code'])) {
$state = md5(uniqid(rand(), true));
$callback = urlencode($redirect_uri);
$wxurl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$appid&redirect_uri=$callback&response_type=code&scope=snsapi_base&state=$state#wechat_redirect";
$this->redirect($wxurl);
}else{
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $appid .'&secret=' . $appkey . '&code=' . $_GET['code'] .'&grant_type=authorization_code';
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $url);
$json = curl_exec($ch);
curl_close($ch);
$arr = json_decode($json, 1);
session('openid',$arr['openid']);
}
}
支付宝支付没有做其它的修改,直接可用