easywechat 配置
public function __construct()
{
$configModel = new Setting();
$this->orderModel = new Orders();
$this->memberModel = new Members();
$config = [
// 必要配置
'app_id' => $configModel->get('ADMIN_APP_ID')->conf_value,
'secret' => $configModel->get('ADMIN_SECRET')->conf_value,
'mch_id' => $configModel->get('ADMIN_MCH_ID')->conf_value,
'key' => $configModel->get('ADMIN_KEY')->conf_value, // API 密钥
// 如需使用敏感接口(如退款、发送红包等)需要配置 API
'cert_path' => $configModel->get('ADMIN_CERT_PATH')->conf_value, // XXX: 绝对路径!!!!
'key_path' => $configModel->get('ADMIN_KEY_PATH')->conf_value, // XXX: 绝对路径!!!!
'notify_url' => '', // 可以在下单时单独设置覆盖它
'log' => [
'level' => 'debug',
'permission' => 0777,
'file' => Env::get('root_path') . 'runtime/wechat/wxpay.log',
],
];
$this->app = Factory::payment($config);
}
下单
参数释义;
body 备注
out_trade_no 商户内部定单号,不能重复,生成示例见附一
total_fee 金额,
notify_url 回调通知地址,此处设置会覆盖掉配置里面的
$param = array(
'body' => '书币',
'out_trade_no' => $order_info['orderno'],
'total_fee' => $order_info['price'],
'notify_url' => $_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'].'/index/notify/notify',
'openid' => $_W['user']['base_openid'],
);
$wxPay = new WXPay();
$res = $wxPay->WeixinPrePay($param);
return json(['data'=>$res,'error'=>0,'message'=>'']);
WXpay
namespace WXPay;
use app\common\model\Members;
use app\common\model\Orders;
use app\common\model\Settlements;
use think\Model;
use EasyWeChat\Payment\Application;
use EasyWeChat\Payment\Order;
use think\Config;
use EasyWeChat\Factory;
use think\facade\Log;
use app\common\model\Setting;
use think\facade\Env;
class WXPay
{
private $orderModel;
private $memberModel;
public function __construct()
{
$configModel = new Setting();
$this->orderModel = new Orders();
$this->memberModel = new Members();
$config = [
// 必要配置
'app_id' => $configModel->get('ADMIN_APP_ID')->conf_value,
'secret' => $configModel->get('ADMIN_SECRET')->conf_value,
'mch_id' => $configModel->get('ADMIN_MCH_ID')->conf_value,
'key' => $configModel->get('ADMIN_KEY')->conf_value, // API 密钥
// 如需使用敏感接口(如退款、发送红包等)需要配置 API
'cert_path' => $configModel->get('ADMIN_CERT_PATH')->conf_value, // XXX: 绝对路径!!!!
'key_path' => $configModel->get('ADMIN_KEY_PATH')->conf_value, // XXX: 绝对路径!!!!
'notify_url' => '', // 可以在下单时单独设置覆盖它
'log' => [
'level' => 'debug',
'permission' => 0777,
'file' => Env::get('root_path') . 'runtime/wechat/wxpay.log',
],
];
$this->app = Factory::payment($config);
}
/**
* 预支付
* @param $data
* @param $openid
* @return array
*/
public function WeixinPrePay($data)
{
$attributes = [
'body' => $data['body'],
'out_trade_no' => $data['out_trade_no'],
'total_fee' => $data['total_fee'] * 100,
'notify_url' => $data['notify_url'], // 回调地址
'trade_type' => 'JSAPI',
'openid' => $data['openid'],
];
$result = $this->app->order->unify($attributes);
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
$prepayId = $result['prepay_id'];
$jssdk = $this->app->jssdk;
$config = $jssdk->sdkConfig($prepayId); //JSSDK 支付参数
$buildConfig = $jssdk->buildConfig(array('chooseWXPay')); //jssdk 支付配置
$json = $jssdk->bridgeConfig($prepayId); //WeixinJSBridge 支付配置
return ['status' => 200, 'msg' => '', 'config' => $config, 'buildConfig' => $buildConfig, 'WeixinJSBridge' => $json];
} else {
return ['status' => 400, 'msg' => '调起支付失败,请稍后尝试'];
}
}
$config jssdk支付参数
$jssdk->bridgeConfig($prepayId); WeixinJSBridge 支付配置
$buildConfig jdsdk配置
html页面发起支付
微信文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
ps:不能拉起支付窗口时请检查当前域名是否在微信商户里js授权列表
1.jssdk 方式
需要先配置 wx.config
<script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js" type="text/javascript" charset="utf-8"></script>
<script src="//code.jquery.com/jquery-3.1.1.min.js"></script>
<script type="text/javascript" charset="utf-8">
wx.config({$buildConfig|raw});
</script>
<body class="bg-secondary">
<button class="btn btn-default1 h50 fs18 mt20 layout1 confirm" id="ok">jssdk确认支付</button>
<button class="btn btn-default1 h50 fs18 mt20 layout1 confirm" id="ok2">JSBridge确认支付</button>
</body>
<script>
//方式一
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {$WeixinJSBridge|raw},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
alert("支付成功");
//window.location.href="__URL__/user_pay_success&order_id=1";
}else{
alert(res.errMsg);
alert("交易失败,请重试");
//window.location.href="__URL__/index?v="+Math.random();
}
}
);
}
$(function(){
// 方式二
$("#ok").click(function(){
wx.chooseWXPay({
timestamp: '{$config['timestamp']}',
nonceStr: '{$config['nonceStr']}',
package: '{$config['package']}',
signType: '{$config['signType']}',
paySign: '{$config['paySign']}', // 支付签名
success: function (res) {
if(res.errMsg == "chooseWXPay:ok" ) {
alert('支付成功。');
//window.location.href="__URL__/user_pay_success&order_id=";
}else{
alert(res.errMsg);
alert("支付失败,请返回重试。");
//window.location.href="__URL__/index?v="+Math.random();
}
},
cancel:function(res){
}
});
})
$("#ok2").click(function(){
onBridgeReady();
})
})
</script>
</html>
2.WeixinJSBridge方式
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {$rspArray['payinfo']},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
// alert("支付成功");
window.location.href="__URL__/user_pay_success&order_id={$order['id']}";
}else{
alert("交易失败,请重试");
window.location.href="__URL__/index?v="+Math.random();
}
}
);
}
//官方示例
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"wx2421b1c4370ec43b", //公众号名称,由商户传入
"timeStamp":"1395712654", //时间戳,自1970年以来的秒数
"nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串
"package":"prepay_id=u802345jgfjsdfgsdg888",
"signType":"MD5", //微信签名方式:
"paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ){
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
}
});
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
支付成功回调
此处根据订单号识别订单类型,然后执行各自逻辑,当没有回复微信时,微信会多次发通知,所以在此处要判断是否处理过,避免重复购买
$order = $this->getInfoByOrderNo($message['out_trade_no']);
if (!$order || $order['status'] == 1) { // 如果订单不存在 或者 订单已经支付过了
return true;
}
/**
* Notes:支付回调,根据订单号前两位判断交易类型,然后交给相应Model处理
* @author:xxf
* Date: 2018/5/8
* Time: 14:46
* @param $type 支付类型
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function notify()
{
$response = $this->app->handlePaidNotify(
function ($message, $fail) {
Log::info('微信支付回调:' . json_encode($message));
if ($message['return_code'] === 'SUCCESS') { // return_code 表示通信状态,不代表支付状态
// 用户是否支付成功
if ($message['result_code'] === 'SUCCESS') {
$type = substr($message['out_trade_no'], 0, 2); //订单号前两位判断支付场景,CX促销活动,CZ普通充值
switch ($type) {
case 'CZ';
$this->orderModel->payRechargeNotify($message);
break;
case 'CX';
$this->orderModel->payActivityNotify($message);
break;
default:
return $fail('单号异常,请稍后再通知我');
break;
}
// 用户支付失败
} elseif ($message['result_code'] === 'FAIL') {
//db('user_orders')->where('id',$message['out_trade_no'])->update('status',7);
}
} else {
return $fail('通信失败,请稍后再通知我');
}
return true; // 返回处理完成
});
$response->send();
}
到此 支付完成
企业付款
企业付款,退款,发送红包等接口必须配合证书路径
public function payToBalance($data)
{
$data = array(
'partner_trade_no' => $data['partner_trade_no'], // 商户订单号
'openid' => $data['openid'],
'check_name' => 'FORCE_CHECK', // NO_CHECK:不校验真实姓名, FORCE_CHECK:强校验真实姓名
're_user_name' => $data['realname'],
'amount' => $data['amount'] * 100, // 付款金额,单位为分
'desc' => $data['desc'], // 付款操作说明信息。必填
);
$result = $this->app->transfer->toBalance($data);
Log::info('付款到余额:' . json_encode($result));
return $result;
}
付款到 红包,银行卡相关接口
WXpay.php
<?php
namespace app\wechatPay;
use think\Model;
use EasyWeChat\Payment\Application;
use EasyWeChat\Payment\Order;
use think\Config;
use EasyWeChat\Factory;
use think\Log;
class Wxpay
{
public function __construct()
{
$config = [
// 必要配置
'app_id' => 'wxc0330dcea20',
'secret' => 'c96af09b',
'mch_id' => '14963',
'key' => '7c44f0413daljoeihoandjnaahdhawee', // API 密钥
// 如需使用敏感接口(如退款、发送红包等)需要配置 API
'cert_path' => ROOT_PATH . 'public/wechat/apiclient_cert.pem', // XXX: 绝对路径!!!!
'key_path' => ROOT_PATH . 'public/wechat/apiclient_key.pem', // XXX: 绝对路径!!!!
'notify_url' => '/user/index/notify', // 可以在下单时单独设置覆盖它
'log' => [
'level' => 'debug',
'permission' => 0777,
'file' => ROOT_PATH . 'runtime/wechat/wxpay.log',
],
];
$this->app = Factory::payment($config);
}
/**
* 预支付
* @param $data
* @param $openid
* @return array
*/
public function WeixinPrePay($data, $openid)
{
Log::init([
'type' => 'File',
'path' => APP_PATH . 'logs/'
]);
$attributes = [
'body' => $data['body'],
'out_trade_no' => $data['out_trade_no'],
'total_fee' => $data['total_fee'] * 100,
'notify_url' => $data['notify_url'], // 回调地址
'trade_type' => 'JSAPI',
'openid' => $openid,
];
$result = $this->app->order->unify($attributes);
Log::info('微信支付参数:' . json_encode($attributes));
Log::info('支付结果:' . json_encode($result));
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
$prepayId = $result['prepay_id'];
$jssdk = $this->app->jssdk;
$config = $jssdk->sdkConfig($prepayId); //JSSDK 支付参数
$buildConfig = $this->app->jssdk->buildConfig(array('chooseWXPay')); //jssdk 支付配置
$json = $jssdk->bridgeConfig($prepayId); //WeixinJSBridge 支付配置
return ['status' => 200, 'msg' => '', 'data' => $config, 'buildConfig' => $buildConfig, 'WeixinJSBridge' => $json];
} else {
return ['status' => 400, 'msg' => '调起支付失败,请稍后尝试'];
}
}
public function buildConfig()
{
$buildConfig = $this->app->jssdk->buildConfig(array('chooseWXPay')); //jssdk 配置
return $buildConfig;
}
/**
* 提现到余额
* Notes:
* @author:xxf
* Date: 2018/5/8
* Time: 14:11
* @param $data
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
*/
public function payToBalance($data)
{
$data = array(
'partner_trade_no' => $data['partner_trade_no'], // 商户订单号
'openid' => $data['openid'],
'check_name' => 'FORCE_CHECK', // NO_CHECK:不校验真实姓名, FORCE_CHECK:强校验真实姓名
're_user_name' => $data['realname'],
'amount' => $data['amount'] * 100, // 付款金额,单位为分
'desc' => $data['desc'], // 付款操作说明信息。必填
);
$result = $this->app->transfer->toBalance($data);
Log::info('付款到余额:' . json_encode($result));
return $result;
}
/**
* 查询付款到零钱的订单
* @param $partnerTradeNo
* @author xingxiong.fei@163.com
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
*/
public function getToBalanceOrder($partnerTradeNo)
{
$result = $this->app->transfer->queryBalanceOrder($partnerTradeNo);
return $result;
}
/**
* 付款到银行卡
* @author xingxiong.fei@163.com
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function payToBankCard()
{
$result = $this->app->transfer->toBankCard([
'partner_trade_no' => '1229222022',
'enc_bank_no' => '6214830901234564', // 银行卡号
'enc_true_name' => 'cc', // 银行卡对应的用户真实姓名
'bank_code' => '1001', // 银行编号
'amount' => 100, // 单位:分
'desc' => '测试',
]);
return $result;
}
/**
* 付款到银行查询
* @param $partnerTradeNo
* @author xingxiong.fei@163.com
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
*/
public function getToBankCardOrder($partnerTradeNo)
{
return $this->app->transfer->queryBankCardOrder($partnerTradeNo);
}
/**
* Notes:发送红包
* Date: 2019/6/26
* Time: 11:03
* @return mixed
*/
public function redPacket()
{
$redpackData = [
'mch_billno' => 'xy123456',
'send_name' => '测试红包',
're_openid' => 'oxTWIuGaIt6gTKsQRLau2M0yL16E',
'total_num' => 1, //固定为1,可不传
'total_amount' => 100, //单位为分,不小于100
'wishing' => '祝福语',
'client_ip' => '192.168.0.1', //可不传,不传则由 SDK 取当前客户端 IP
'act_name' => '测试活动',
'remark' => '测试备注',
// ...
];
$result = $this->app->redpack->sendNormal($redpackData);
return $result;
}
/**
* Notes:支付回调
* @author:xxf
* Date: 2018/5/8
* Time: 14:46
* @param $type 支付类型
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function notify()
{
Log::init([
'type' => 'File',
'path' => APP_PATH . 'logs/'
]);
$response = $this->app->handlePaidNotify(
function ($message, $fail) {
Log::info('微信支付回调:' . json_encode($message));
$type = substr($message['out_trade_no'], 0, 2); //订单号前两位判断何种支付,CC乘车 BX保险 YH购买优惠券 BZ 保证金
//每日收入
$date = date("Y-m-d", time());
$temp = db('profit_day')->where('day', $date)->find();
if (!$temp)
{
$data['day'] = $date;
db('profit_day')->insert($data);
}
/*if ($type == 'CC'){
$order = db('user_orders')->where('order_number',$message['out_trade_no'])->find();
}
$order = db('user_orders')->where('order_number',$message['out_trade_no'])->find();
if (!$order || $order['status']==7) { // 如果订单不存在 或者 订单已经支付过了
return true;
}*/
if ($message['return_code'] === 'SUCCESS') { // return_code 表示通信状态,不代表支付状态
// 用户是否支付成功
if ($message['result_code'] === 'SUCCESS') {
switch ($type) {
case 'CC';
$order = db('user_orders')->where('order_number', $message['out_trade_no'])->find();
if (!$order || $order['orderStatus'] == 6) { // 如果订单不存在 或者 订单已经支付过了
return true;
}
db('user_orders')->where('order_number', $message['out_trade_no'])->update(['orderStatus' => 6]);
db('profit_day')->where('day', $date)->setInc('pay_profit', $message['total_fee']/100);
break;
case 'YH';
$order = db('coupon_user')->where('order_number', $message['out_trade_no'])->find();
if (!$order || $order['status'] == 1) {
return true;
}
db('coupon_user')->where('order_number', $message['out_trade_no'])->update(['status' => 1]);
db('profit_day')->where('day', $date)->setInc('coupon_profit', $message['total_fee']/100);
break;
case 'BZ';
$order = db('bail')->where('order_number', $message['out_trade_no'])->find();
if (!$order || $order['status'] == 1) {
return true;
}
db('bail')->where('order_number', $message['out_trade_no'])->update(['pay_status' => 1]);
db('user')->where('uid', $order['uid'])->setInc('cancel_times');
break;
case 'BX';
$order = db('insurance_user')->where('order_number', $message['out_trade_no'])->find();
if (!$order || $order['status'] == 1) {
return true;
}
db('insurance_user')->where('order_number', $message['out_trade_no'])->update(['status' => 1]);
db('profit_day')->where('day', $date)->setInc('insurance_profit', $message['total_fee']/100);
break;
}
/*
db('user_orders')->where('order_number',$message['out_trade_no'])->update(['status' => 7]);*/
// 用户支付失败
} elseif ($message['result_code'] === 'FAIL') {
//db('user_orders')->where('id',$message['out_trade_no'])->update('status',7);
}
} else {
return $fail('通信失败,请稍后再通知我');
}
return true; // 返回处理完成
});
$response->send();
}
}
附一 生成订单号
/**
* 生成订单号
* Time: 11:15
* @param $type 订单类型
* @return string
*/
function makeOrdersn($type='')
{
@date_default_timezone_set("PRC");
//订单号码主体(YYYYMMDDHHIISSNNNNNNNN)
$order_id_main = $type.date('YmdHis') . rand(10000000,99999999);
$order_id_len = strlen($order_id_main);
$order_id_sum = 0;
for($i=0; $i<$order_id_len; $i++){
$order_id_sum += (int)(substr($order_id_main,$i,1));
}
//唯一订单号码(YYYYMMDDHHIISSNNNNNNNNCC)
$order_id = $order_id_main . str_pad((100 - $order_id_sum % 100) % 100,2,'0',STR_PAD_LEFT);
return $order_id;
}