1164 lines
45 KiB
PHP
Executable File
1164 lines
45 KiB
PHP
Executable File
<?php
|
||
namespace hema\wechat;
|
||
|
||
use app\common\model\Setting;
|
||
use hema\wechat\AesUtil;
|
||
use think\facade\Cache;
|
||
use hema\Http;
|
||
use app\common\model\Applet;
|
||
use app\common\model\Record;
|
||
use app\common\model\DivideAccount;
|
||
|
||
/**
|
||
* 微信支付
|
||
*/
|
||
class Pay
|
||
{
|
||
private $api_url = 'https://api.mch.weixin.qq.com';//接口域名
|
||
private $version = 'v3'; //接口版本
|
||
private $isp_config; // 微信支付服务商参数
|
||
private $config; // 微信支付参数
|
||
private $error;
|
||
/**
|
||
* 构造方法
|
||
*/
|
||
public function __construct(array $config = [])
|
||
{
|
||
$this->isp_config = Setting::getItem('wxpayisp',0);
|
||
$this->config = $config;
|
||
}
|
||
|
||
/********** V3接口 **********/
|
||
|
||
/**
|
||
* H5下单API
|
||
* $out_trade_no=订单号, $total=支付金额,,$attach=订单描述
|
||
* $profit_sharing=是否分账(有配送费要分账时传递)
|
||
*/
|
||
public function h5($out_trade_no,$total,$notify_url,$attach='订单支付',$profit_sharing = false)
|
||
{
|
||
$params = [
|
||
'description' => $attach,//商品描述
|
||
'out_trade_no' => $out_trade_no,//商户订单号
|
||
'attach' => $attach,//附加数据
|
||
'notify_url' => base_url() . $notify_url, //通知地址
|
||
'amount' => [
|
||
'total' => intval($total * 100),//订单总金额,单位为分
|
||
],
|
||
'scene_info' => [
|
||
'payer_client_ip' => \request()->ip(),//用户终端IP
|
||
],
|
||
'h5_info' => [
|
||
'type' => 'Wap'
|
||
]
|
||
];
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商
|
||
$params['sp_appid'] = $this->isp_config['app_id'];//服务商应用ID
|
||
$params['sp_mchid'] = $this->isp_config['mch_id'];//服务商商户号
|
||
$params['sub_appid'] = $this->config['app_id'];//子商户应用ID
|
||
$params['sub_mchid'] = $this->config['mch_id'];//子商户号
|
||
$url = $this->getUrl('pay/partner/transactions/h5');//服务商
|
||
$is_isp = true;
|
||
}else{
|
||
//直连商户
|
||
$params['appid'] = $this->config['app_id'];//小程序ID
|
||
$params['mchid'] = $this->config['mch_id'];//商户号
|
||
$url = $this->getUrl('pay/transactions/h5');//直连商户
|
||
$is_isp = false;
|
||
}
|
||
//判断是否开启分账
|
||
$divide = Setting::getItem('divide',0);
|
||
if($profit_sharing or $divide['extract'] > 0){
|
||
$params['settle_info']['profit_sharing'] = true; //开启分账
|
||
}
|
||
$params = hema_json($params);
|
||
$headers = [
|
||
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $this->sign($url,'POST',$params,$is_isp),
|
||
'Content-Type:application/json',
|
||
'Accept:application/json',
|
||
'User-Agent:' . $is_isp?$this->isp_config['mch_id']:$this->config['mch_id'],
|
||
];
|
||
$result = json_decode(Http::post($url, $params,[],$headers),true);
|
||
if(isset($result['code'])){
|
||
$this->error = 'code:' . $result['code'] . ',msg:' . $result['message'];
|
||
return false;
|
||
}
|
||
return $result['h5_url'];
|
||
}
|
||
//扣取手续费 按照0.6%计算
|
||
private function serviceFee($fee)
|
||
{
|
||
return intval(($fee - ($fee * 6 / 1000)) * 100);
|
||
}
|
||
/**
|
||
* 分账
|
||
* $data:数组 =分账数据
|
||
* [
|
||
* out_order_no:第三方订单号
|
||
* transaction_id:微信订单号
|
||
* total:分账总金额
|
||
* ]
|
||
* $applet_id=小程序编号
|
||
* $delivery_fee=配送费分账金额
|
||
*/
|
||
public function divide($data,$applet_id='',$delivery_fee=0)
|
||
{
|
||
$is_divide = false;//是否分账
|
||
$total = $data['total'];//分账总金额
|
||
$service_fee = 0;//平台分账金额(单位分)
|
||
$agent_fee = 0;//代理分账金额(单位分)
|
||
$agent_openid = '';//代理收款账号
|
||
//判断外卖订单是否分账配送费
|
||
if($delivery_fee > 0){
|
||
$total = $total - $delivery_fee;
|
||
$delivery_fee = $this->serviceFee($delivery_fee);//去掉手续费
|
||
$is_divide = true; //配送费大于0 开启分账
|
||
}
|
||
$divide = Setting::getItem('divide',0); //分佣参数
|
||
$applet = Applet::get($applet_id);//获取商家应用
|
||
//如果开启分佣
|
||
if($divide['extract'] > 0){
|
||
$extract = $total * $divide['extract'] / 100;//抽取金额
|
||
$service_fee = $this->serviceFee($extract);//去掉手续费
|
||
if($divide['agent_extract'] > 0){
|
||
//判断商家是否有代理商
|
||
if($applet['agent_id'] > 0){
|
||
if($account = DivideAccount::withoutGlobalScope()->where('applet_id',$applet_id)->find()){
|
||
if(!empty($account['open_id'])){
|
||
$agent_openid = $account['open_id'];
|
||
$agent_fee = $this->serviceFee($extract * $divide['agent_extract'] / 100);//去掉手续费
|
||
$service_fee = $service_fee - $agent_fee;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
$is_divide = true; //开启分账
|
||
}
|
||
//判断是否要进行分账
|
||
if(!$is_divide){
|
||
$this->error = '不用分账';
|
||
return false;
|
||
}
|
||
//***************** 添加分账接收方 *********************//
|
||
$receivers = [];//收款方账号列表
|
||
//添加平台收佣账号
|
||
if(($service_fee + $delivery_fee) > 0){
|
||
if(!$this->addReceivers($applet_id)) {
|
||
$this->error = '添加平台分账接收方失败';
|
||
return false;
|
||
}
|
||
$webpay = Setting::getItem('webpay',0)['wx']; //平台微信支付参数
|
||
$receivers[] = [
|
||
'type' => 'MERCHANT_ID',//分账接收方类型 MERCHANT_ID=商户号 PERSONAL_OPENID=个人openid
|
||
'account' => $webpay['mch_id'],//分账接收方账号
|
||
'amount' => $service_fee + $delivery_fee,//分账金额
|
||
'description' => '分佣给平台',//分账描述
|
||
];
|
||
}
|
||
//添加代理收佣账号
|
||
if($agent_fee > 0 and !empty($agent_openid)){
|
||
if(!$this->addReceivers($applet_id,false,$agent_openid)) {
|
||
$this->error = '添加代理分账接收方失败';
|
||
return false;
|
||
}
|
||
$receivers[] = [
|
||
'type' => $this->config['is_sub'] == 1 ? 'PERSONAL_SUB_OPENID':'PERSONAL_OPENID',//分账接收方类型
|
||
'account' => $agent_openid,//分账接收方账号
|
||
'amount' => $agent_fee,//分账金额
|
||
'description' => '分佣给代理',//分账描述
|
||
];
|
||
}
|
||
//***************** 请求分账 *********************//
|
||
if(sizeof($receivers) == 0){
|
||
$this->error = '收款方账号列表为空';
|
||
return false;
|
||
}
|
||
if(!$this->profitSharing($data['transaction_id'],$data['out_order_no'],$receivers)) {
|
||
$this->error = '请求分账失败';
|
||
return false;
|
||
}
|
||
//***************** 添加交易记录 *********************//
|
||
$record_log = [];//交易流水记录
|
||
//是否增加平台分红记录(分佣)
|
||
if(($service_fee - $agent_fee) > 0){
|
||
$money = sprintf("%.2f",$service_fee / 100);//计算金额
|
||
//平台分红(分佣)记录
|
||
array_push($record_log,[
|
||
'mode' => 40, //赠送
|
||
'type' => 30, //微信
|
||
'order_no' => $data['out_order_no'],
|
||
'money' => $money,
|
||
'remark' => '交易分佣'
|
||
]);
|
||
//商户扣费记录
|
||
array_push($record_log,[
|
||
'mode' => 50, //扣减
|
||
'type' => 30, //微信
|
||
'order_no' => $data['out_order_no'],
|
||
'money' => $money,
|
||
'user_id' => $applet['user_id'],
|
||
'remark' => '交易服务费'
|
||
]);
|
||
}
|
||
//是否增加配送费记录
|
||
if($delivery_fee > 0){
|
||
$money = sprintf("%.2f",$delivery_fee / 100);//计算金额
|
||
//平台收取记录
|
||
array_push($record_log,[
|
||
'mode' => 40, //赠送
|
||
'type' => 30, //微信
|
||
'order_no' => $data['out_order_no'],
|
||
'money' => $money,
|
||
'remark' => '第三方配送费'
|
||
]);
|
||
//商户扣费记录
|
||
array_push($record_log,[
|
||
'mode' => 50, //扣减
|
||
'type' => 30, //微信
|
||
'order_no' => $data['out_order_no'],
|
||
'money' => $money,
|
||
'user_id' => $applet['user_id'],
|
||
'remark' => '第三方配送费'
|
||
]);
|
||
}
|
||
//是否增加代理分佣记录
|
||
if($agent_fee > 0){
|
||
$money = sprintf("%.2f",$agent_fee / 100);//计算金额
|
||
//平台收取记录
|
||
array_push($record_log,[
|
||
'mode' => 40, //赠送
|
||
'type' => 30, //微信
|
||
'order_no' => $data['out_order_no'],
|
||
'money' => $money,
|
||
'user_id' => $applet['agent_id'],
|
||
'remark' => '交易分佣'
|
||
]);
|
||
}
|
||
//批量增加交易记录
|
||
$model = new Record;
|
||
if(!$model->saveAll($record_log)){
|
||
$this->error = '添加交易记录失败';
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
/**
|
||
* 请求分账API
|
||
*/
|
||
private function profitSharing($transaction_id,$out_order_no,$receivers)
|
||
{
|
||
//服务商
|
||
$params = [
|
||
'transaction_id' => $transaction_id,//微信订单号
|
||
'out_order_no' => $out_order_no,//商户分账单号
|
||
'receivers' => $receivers,
|
||
'unfreeze_unsplit' => true,//是否解冻剩余未分资金
|
||
];
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商
|
||
$params['appid'] = $this->isp_config['app_id'];//服务商应用ID
|
||
$params['sub_appid'] = $this->config['app_id'];//子商户应用ID
|
||
$params['sub_mchid'] = $this->config['mch_id'];//子商户号
|
||
$is_isp = true;
|
||
}else{
|
||
//直连商户
|
||
$params['appid'] = $this->config['app_id'];//小程序ID
|
||
$is_isp = false;
|
||
}
|
||
$params = hema_json($params);
|
||
$url = $this->getUrl('profitsharing/orders');
|
||
$headers = [
|
||
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $this->sign($url,'POST',$params,$is_isp),
|
||
'Content-Type:application/json',
|
||
'Accept:application/json',
|
||
'User-Agent:' . $is_isp?$this->isp_config['mch_id']:$this->config['mch_id'],
|
||
'Wechatpay-Serial:' . $this->isp_config['serial_no'],
|
||
];
|
||
return $this->result(json_decode(Http::post($url, $params,[],$headers),true));
|
||
}
|
||
/**
|
||
* 添加分账接收方API
|
||
* $is_mchid = 接收方是否是商户 $account = 接收账号
|
||
*/
|
||
private function addReceivers($applet_id,$is_mchid=true,$account='')
|
||
{
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商
|
||
$params['appid'] = $this->isp_config['app_id'];//服务商应用ID
|
||
$params['sub_appid'] = $this->config['app_id'];//子商户应用ID
|
||
$params['sub_mchid'] = $this->config['mch_id'];//子商户号
|
||
if($is_mchid){
|
||
$webpay = Setting::getItem('webpay',0)['wx'];
|
||
$params['type'] = 'MERCHANT_ID';//分账接收方类型
|
||
$params['account'] = $webpay['mch_id'];//分账接收方账号
|
||
$params['name'] = $this->getEncrypt($webpay['name']); //(加密)分账个人接收方姓名 分账接收方类型是MERCHANT_ID时,是商户全称(必传)
|
||
}else{
|
||
$params['type'] = 'PERSONAL_SUB_OPENID';//分账接收方类型
|
||
$params['account'] = $account;//分账接收方账号
|
||
}
|
||
$params['relation_type'] = 'SERVICE_PROVIDER'; //与分账方的关系类型 服务商
|
||
$is_isp = true;
|
||
$serial_no = $this->isp_config['serial_no'];
|
||
}else{
|
||
//直连商户
|
||
$params['appid'] = $this->config['app_id'];//小程序ID
|
||
if($is_mchid){
|
||
$webpay = Setting::getItem('webpay',0)['wx'];
|
||
$params['type'] = 'MERCHANT_ID';//分账接收方类型
|
||
$params['account'] = $webpay['mch_id'];//分账接收方账号
|
||
$params['name'] = $this->getEncrypt($webpay['name'],false,$applet_id); //(加密)分账个人接收方姓名 分账接收方类型是MERCHANT_ID时,是商户全称(必传)
|
||
}else{
|
||
$params['type'] = 'PERSONAL_OPENID';//分账接收方类型
|
||
$params['account'] = $account;//分账接收方账号
|
||
}
|
||
$params['relation_type'] = 'PARTNER'; ////与分账方的关系类型 合作伙伴
|
||
$is_isp = false;
|
||
$serial_no = $this->config['serial_no'];
|
||
}
|
||
$params = hema_json($params);
|
||
$url = $this->getUrl('profitsharing/receivers/add');
|
||
$headers = [
|
||
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $this->sign($url,'POST',$params,$is_isp),
|
||
'Content-Type:application/json',
|
||
'Accept:application/json',
|
||
'User-Agent:' . $is_isp?$this->isp_config['mch_id']:$this->config['mch_id'],
|
||
'Wechatpay-Serial:' . $serial_no,
|
||
];
|
||
return $this->result(json_decode(Http::post($url, $params,[],$headers),true));
|
||
}
|
||
/**
|
||
* 申请退款API
|
||
*/
|
||
public function refunds($transaction_id,$out_refund_no,$refund_fee,$total_fee,$notify_url='',$reason='')
|
||
{
|
||
$params = [
|
||
'transaction_id' => $transaction_id,//微信支付订单号
|
||
'out_refund_no' => $out_refund_no,//退款订单号
|
||
'amount' => [
|
||
'refund' => intval($refund_fee * 100), // 退款金额,价格:单位分
|
||
'total' => intval($total_fee * 100), // 订单金额,价格:单位分
|
||
'currency' => 'CNY', //退款币种 只支持人民币:CNY
|
||
],
|
||
];
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商
|
||
$params['sub_mchid'] = $this->config['mch_id'];//子商户号
|
||
$is_isp = true;
|
||
}else{
|
||
$is_isp = false;
|
||
}
|
||
!empty($reason) && $params['reason'] = $reason;//退款原因
|
||
!empty($notify_url) && $params['notify_url'] = base_url() . $notify_url; // 异步通知地址
|
||
|
||
$params = hema_json($params);
|
||
$url = $this->getUrl('refund/domestic/refunds');
|
||
$headers = [
|
||
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $this->sign($url,'POST',$params,$is_isp),
|
||
'Content-Type:application/json',
|
||
'Accept:application/json',
|
||
'User-Agent:' . $is_isp?$this->isp_config['mch_id']:$this->config['mch_id'],
|
||
];
|
||
return $this->result(json_decode(Http::post($url, $params,[],$headers),true));
|
||
|
||
}
|
||
/**
|
||
* 退款成功异步通知
|
||
*/
|
||
public function refundsNotify($Model,$applet_id='')
|
||
{
|
||
//接收微信服务器回调的数据流
|
||
if (!$json = file_get_contents('php://input')) {
|
||
$this->returnHttpCode(false);
|
||
}
|
||
// 将服务器返回的json数据转化为数组
|
||
$result = json_decode($json,true);
|
||
if(empty($applet_id)){
|
||
$this->config = Setting::getItem('webpay',0)['wx'];//平台商户支付参数
|
||
}else{
|
||
$this->config = Setting::getItem('wxpay',$applet_id);//商家商户支付参数
|
||
}
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商
|
||
$api_key = $this->isp_config['api_key'];
|
||
//判断平台证书是否过期
|
||
if($this->isp_config['expire_time'] < time()){
|
||
//更新平台证书
|
||
if(!$this->certificates()){
|
||
$this->returnHttpCode(false,$this->error);//更新失败
|
||
}
|
||
}
|
||
}else{
|
||
//直连商户
|
||
$api_key = $this->config['api_key'];
|
||
//判断平台证书是否过期
|
||
if($this->config['expire_time'] < time()){
|
||
//更新平台证书
|
||
if(!$this->certificates(false,$applet_id)){
|
||
$this->returnHttpCode(false,$this->error);//更新失败
|
||
}
|
||
}
|
||
}
|
||
if(!$decrypt = new AesUtil($api_key)){
|
||
$this->returnHttpCode(false,$decrypt->getError());
|
||
}
|
||
if(!$res = $decrypt->decryptToString($result['resource']['associated_data'], $result['resource']['nonce'], $result['resource']['ciphertext'])){
|
||
$this->returnHttpCode(false,$decrypt->getError());
|
||
}
|
||
$data = json_decode($res,true);
|
||
// 订单信息
|
||
if(!$order = $Model->refundDetail($data['out_refund_no'])){
|
||
$this->returnHttpCode(false,'订单不存在');
|
||
}
|
||
|
||
if($data['refund_status'] == 'SUCCESS') {
|
||
// 更新订单状态
|
||
$order->updateRefundStatus($data['refund_id']);
|
||
$this->returnHttpCode(true);// 返回状态
|
||
}
|
||
$this->returnHttpCode(false, '退款失败');
|
||
}
|
||
/**
|
||
* Native下单API
|
||
* $out_trade_no=订单号, $total=支付金额,$attach=订单描述 $profit_sharing=是否分账
|
||
*/
|
||
public function native($out_trade_no,$total,$notify_url,$attach='订单支付',$profit_sharing = false)
|
||
{
|
||
$params = [
|
||
'description' => $attach,//商品描述
|
||
'out_trade_no' => $out_trade_no,//商户订单号
|
||
'attach' => $attach,//附加数据
|
||
'notify_url' => base_url() . $notify_url, //通知地址
|
||
'amount' => [
|
||
'total' => intval($total * 100),//订单总金额,单位为分
|
||
],
|
||
'scene_info' => [
|
||
'payer_client_ip' => \request()->ip(),//用户终端IP
|
||
],
|
||
];
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商
|
||
$params['sp_appid'] = $this->isp_config['app_id'];//服务商应用ID
|
||
$params['sp_mchid'] = $this->isp_config['mch_id'];//服务商商户号
|
||
//$params['sub_appid'] = $this->config['app_id'];//子商户应用ID
|
||
$params['sub_mchid'] = $this->config['mch_id'];//子商户号
|
||
//$params['payer']['sub_openid'] = $openid; //子用户标识Openid
|
||
$url = $this->getUrl('pay/partner/transactions/native');//服务商
|
||
$is_isp = true;
|
||
}else{
|
||
//直连商户
|
||
$params['appid'] = $this->config['app_id'];//小程序ID
|
||
$params['mchid'] = $this->config['mch_id'];//商户号
|
||
$url = $this->getUrl('pay/transactions/native');//直连商户
|
||
$is_isp = false;
|
||
}
|
||
//判断是否开启分账
|
||
$divide = Setting::getItem('divide',0);
|
||
if($profit_sharing or $divide['extract'] > 0){
|
||
$params['settle_info']['profit_sharing'] = true; //开启分账
|
||
}
|
||
$params = hema_json($params);
|
||
$headers = [
|
||
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $this->sign($url,'POST',$params,$is_isp),
|
||
'Content-Type:application/json',
|
||
'Accept:application/json',
|
||
'User-Agent:' . $is_isp?$this->isp_config['mch_id']:$this->config['mch_id'],
|
||
];
|
||
$result = json_decode(Http::post($url, $params,[],$headers),true);
|
||
if(isset($result['code'])){
|
||
$this->error = 'code:' . $result['code'] . ',msg:' . $result['message'];
|
||
return false;
|
||
}
|
||
if(!isset($result['code_url'])){
|
||
$this->error = 'Native下单接口请求失败';
|
||
return false;
|
||
}
|
||
return $result['code_url'];
|
||
}
|
||
/**
|
||
* JSAPI下单API
|
||
* $out_trade_no=订单号, $total=支付金额,$openid=微信用户ID, ,$attach=订单描述
|
||
* $profit_sharing=是否分账(有配送费要分账时传递)
|
||
*/
|
||
public function jsapi($out_trade_no,$total,$openid,$notify_url,$attach='订单支付',$profit_sharing = false)
|
||
{
|
||
$params = [
|
||
'description' => $attach,//商品描述
|
||
'attach' => $attach,//附加数据
|
||
'out_trade_no' => $out_trade_no,//商户订单号
|
||
'notify_url' => base_url() . $notify_url, //通知地址
|
||
'amount' => [
|
||
'total' => intval($total * 100),//订单总金额,单位为分
|
||
],
|
||
'scene_info' => [
|
||
'payer_client_ip' => \request()->ip(),//用户终端IP
|
||
],
|
||
];
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商
|
||
$params['sp_appid'] = $this->isp_config['app_id'];//服务商应用ID
|
||
$params['sp_mchid'] = $this->isp_config['mch_id'];//服务商商户号
|
||
$params['sub_appid'] = $this->config['app_id'];//子商户应用ID
|
||
$params['sub_mchid'] = $this->config['mch_id'];//子商户号
|
||
$params['payer']['sub_openid'] = $openid; //子用户标识Openid
|
||
$url = $this->getUrl('pay/partner/transactions/jsapi');//服务商
|
||
$is_isp = true;
|
||
}else{
|
||
//直连商户
|
||
$params['appid'] = $this->config['app_id'];//小程序ID
|
||
$params['mchid'] = $this->config['mch_id'];//商户号
|
||
$params['payer']['openid'] = $openid; //用户标识Openid
|
||
$url = $this->getUrl('pay/transactions/jsapi');//直连商户
|
||
$is_isp = false;
|
||
}
|
||
//判断是否开启分账
|
||
$divide = Setting::getItem('divide',0);
|
||
if($profit_sharing or $divide['extract'] > 0){
|
||
$params['settle_info']['profit_sharing'] = true; //开启分账
|
||
}
|
||
$params = hema_json($params);
|
||
$headers = [
|
||
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $this->sign($url,'POST',$params,$is_isp),
|
||
'Content-Type:application/json',
|
||
'Accept:application/json',
|
||
'User-Agent:' . $is_isp?$this->isp_config['mch_id']:$this->config['mch_id'],
|
||
];
|
||
$result = json_decode(Http::post($url, $params,[],$headers),true);
|
||
if(isset($result['code'])){
|
||
$this->error = 'code:' . $result['code'] . ',msg:' . $result['message'];
|
||
return false;
|
||
}
|
||
if(!isset($result['prepay_id'])){
|
||
$this->error = 'JSAPI下单接口请求失败';
|
||
return false;
|
||
}
|
||
$data = [
|
||
'timeStamp' => (string)time(),
|
||
'nonceStr' => $this->nonce(),
|
||
'package' => 'prepay_id=' . $result['prepay_id'],
|
||
'signType' => 'RSA',
|
||
];
|
||
$data['paySign'] = $this->paySign($data);
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* 支付成功异步通知
|
||
*/
|
||
public function notify($Model,$applet_id,$method='edit')
|
||
{
|
||
//接收微信服务器回调的数据流
|
||
if (!$json = file_get_contents('php://input')) {
|
||
$this->returnHttpCode(false);
|
||
}
|
||
// 将服务器返回的json数据转化为数组
|
||
$result = json_decode($json,true);
|
||
if(empty($applet_id)){
|
||
$this->config = Setting::getItem('webpay',0)['wx'];//平台商户支付参数
|
||
}else{
|
||
$this->config = Setting::getItem('wxpay',$applet_id);//商家商户支付参数
|
||
}
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商
|
||
$api_key = $this->isp_config['api_key'];
|
||
//判断平台证书是否过期
|
||
if($this->isp_config['expire_time'] < time()){
|
||
//更新平台证书
|
||
if(!$this->certificates()){
|
||
$this->returnHttpCode(false,$this->error);//更新失败
|
||
}
|
||
}
|
||
}else{
|
||
//直连商户
|
||
$api_key = $this->config['api_key'];
|
||
//判断平台证书是否过期
|
||
if($this->config['expire_time'] < time()){
|
||
//更新平台证书
|
||
if(!$this->certificates(false,$applet_id)){
|
||
$this->returnHttpCode(false,$this->error);//更新失败
|
||
}
|
||
}
|
||
}
|
||
if(!$decrypt = new AesUtil($api_key)){
|
||
$this->returnHttpCode(false,$decrypt->getError());
|
||
}
|
||
if(!$res = $decrypt->decryptToString($result['resource']['associated_data'], $result['resource']['nonce'], $result['resource']['ciphertext'])){
|
||
$this->returnHttpCode(false,$decrypt->getError());
|
||
}
|
||
$data = json_decode($res,true);
|
||
// 订单信息
|
||
if(!$order = $Model->payDetail($data['out_trade_no'])){
|
||
$this->returnHttpCode(false,'订单不存在');
|
||
}
|
||
|
||
//判断支付状态
|
||
if($data['trade_state'] == 'SUCCESS') {
|
||
if($method == 'add'){
|
||
$Model->updatePayStatus($data['transaction_id'],$order);
|
||
Cache::delete($data['out_trade_no']);
|
||
}else{
|
||
// 更新订单状态
|
||
$order->updatePayStatus($data['transaction_id']);
|
||
}
|
||
// 返回状态
|
||
$this->returnHttpCode(true);
|
||
}
|
||
// 返回状态
|
||
$this->returnHttpCode(false, '支付失败');
|
||
}
|
||
|
||
/**
|
||
* 特约商户进件
|
||
* 频率限制:15/s
|
||
*/
|
||
public function applyment($params)
|
||
{
|
||
//********************* 数据加密 *****************
|
||
|
||
//管理员姓名
|
||
if(!$result = $this->getEncrypt($params['contact_info']['contact_name'])){
|
||
return false;
|
||
}
|
||
$params['contact_info']['contact_name'] = $result;
|
||
//管理员电话
|
||
if(!$result = $this->getEncrypt($params['contact_info']['mobile_phone'])){
|
||
return false;
|
||
}
|
||
$params['contact_info']['mobile_phone'] = $result;
|
||
//管理员邮箱
|
||
if(!$result = $this->getEncrypt($params['contact_info']['contact_email'])){
|
||
return false;
|
||
}
|
||
$params['contact_info']['contact_email'] = $result;
|
||
//身份证姓名
|
||
if(!$result = $this->getEncrypt($params['subject_info']['identity_info']['id_card_info']['id_card_name'])){
|
||
return false;
|
||
}
|
||
$params['subject_info']['identity_info']['id_card_info']['id_card_name'] = $result;
|
||
//身份证号
|
||
if(!$result = $this->getEncrypt($params['subject_info']['identity_info']['id_card_info']['id_card_number'])){
|
||
return false;
|
||
}
|
||
$params['subject_info']['identity_info']['id_card_info']['id_card_number'] = $result;
|
||
|
||
//身份证居住地址
|
||
if(!$result = $this->getEncrypt($params['subject_info']['identity_info']['id_card_info']['id_card_address'])){
|
||
return false;
|
||
}
|
||
$params['subject_info']['identity_info']['id_card_info']['id_card_address'] = $result;
|
||
|
||
|
||
//银行开户名称
|
||
if(!$result = $this->getEncrypt($params['bank_account_info']['account_name'])){
|
||
return false;
|
||
}
|
||
$params['bank_account_info']['account_name'] = $result;
|
||
//银行账号
|
||
if(!$result = $this->getEncrypt($params['bank_account_info']['account_number'])){
|
||
return false;
|
||
}
|
||
$params['bank_account_info']['account_number'] = $result;
|
||
|
||
//********************* 上传图片 *****************
|
||
//营业执照
|
||
if(!$result = $this->upload($params['subject_info']['business_license_info']['license_copy'])){
|
||
return false;
|
||
}
|
||
$params['subject_info']['business_license_info']['license_copy'] = $result;
|
||
//身份证正面
|
||
if(!$result = $this->upload($params['subject_info']['identity_info']['id_card_info']['id_card_copy'])){
|
||
return false;
|
||
}
|
||
$params['subject_info']['identity_info']['id_card_info']['id_card_copy'] = $result;
|
||
//身份证反面
|
||
if(!$result = $this->upload($params['subject_info']['identity_info']['id_card_info']['id_card_national'])){
|
||
return false;
|
||
}
|
||
$params['subject_info']['identity_info']['id_card_info']['id_card_national'] = $result;
|
||
//特殊资质
|
||
if(!$result = $this->upload($params['settlement_info']['qualifications'][0])){
|
||
return false;
|
||
}
|
||
$params['settlement_info']['qualifications'][0] = $result;
|
||
//门头照片
|
||
if(!$result = $this->upload($params['business_info']['sales_info']['biz_store_info']['store_entrance_pic'][0])){
|
||
return false;
|
||
}
|
||
$params['business_info']['sales_info']['biz_store_info']['store_entrance_pic'][0] = $result;
|
||
//店内照片
|
||
if(!$result = $this->upload($params['business_info']['sales_info']['biz_store_info']['indoor_pic'][0])){
|
||
return false;
|
||
}
|
||
$params['business_info']['sales_info']['biz_store_info']['indoor_pic'][0] = $result;
|
||
$params = hema_json($params);
|
||
$url = $this->getUrl('applyment4sub/applyment/');
|
||
$headers = [
|
||
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $this->sign($url,'POST',$params),
|
||
'Content-Type:application/json',
|
||
'Accept:application/json',
|
||
'User-Agent:' . $this->isp_config['mch_id'],
|
||
'Wechatpay-Serial:' . $this->isp_config['serial_no'],
|
||
];
|
||
$result = json_decode(Http::post($url, $params,[],$headers),true);
|
||
if(isset($result['code'])){
|
||
$this->error = 'code:' . $result['code'] . ',msg:' . $result['message'];
|
||
return false;
|
||
}
|
||
return $result['applyment_id'];
|
||
}
|
||
/**
|
||
* 查询申请单状态
|
||
*/
|
||
public function queryApplyment($no,$is_applyment_id = false)
|
||
{
|
||
if($is_applyment_id){
|
||
$path = 'applyment_id/' . $no;//通过申请单号查询申请状态(官方返回的编号)
|
||
}else{
|
||
$path = 'business_code/' . $no;//通过业务申请编号查询申请状态(第三方自定义的编号)
|
||
}
|
||
$url = $this->getUrl('applyment4sub/applyment/'.$path);
|
||
$headers = [
|
||
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $this->sign($url,'GET'),
|
||
'Accept:application/json',
|
||
];
|
||
return $this->result(json_decode(Http::get($url, [],[],$headers),true));
|
||
}
|
||
/**
|
||
* 获取平台证书列表
|
||
*/
|
||
private function certificates($is_isp=true,$applet_id='')
|
||
{
|
||
$url = $this->getUrl('certificates');
|
||
$headers = [
|
||
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $this->sign($url,'GET','',$is_isp),
|
||
'Accept:application/json',
|
||
'User-Agent:https://zh.wikipedia.org/wiki/User_agent',
|
||
];
|
||
$result = json_decode(Http::get($url,[],[],$headers),true);
|
||
if(isset($result['code'])){
|
||
$this->error = 'code:' . $result['code'] . ',msg:' . $result['message'];
|
||
return false;
|
||
}
|
||
//验证是否获取到了数据
|
||
if(!isset($result['data']) and sizeof($result['data']) == 0){
|
||
$this->error = '未获取到可用的平台证书';
|
||
return false;
|
||
}
|
||
$result = $result['data'][0];//获取证书列表中的第一个数据
|
||
if($is_isp){
|
||
$api_key = $this->isp_config['api_key'];
|
||
}else{
|
||
$api_key = $this->config['api_key'];
|
||
}
|
||
if(!$decrypt = new AesUtil($api_key)){
|
||
$this->error = $decrypt->getError();
|
||
return false;
|
||
}
|
||
if(!$res = $decrypt->decryptToString($result['encrypt_certificate']['associated_data'], $result['encrypt_certificate']['nonce'], $result['encrypt_certificate']['ciphertext'])){
|
||
$this->error = $decrypt->getError();
|
||
return false;
|
||
}
|
||
//计算到期时间
|
||
$expire_time = explode('T',$result['expire_time']);
|
||
$expire_time = strtotime($expire_time[0]);
|
||
$model = new Setting;
|
||
//更新平台证书
|
||
if($is_isp){
|
||
$this->isp_config['certificates'] = $res;
|
||
$this->isp_config['serial_no'] = $result['serial_no'];
|
||
$this->isp_config['expire_time'] = $expire_time;
|
||
$model->edit('wxpayisp',$this->isp_config,0); //保存到数据库
|
||
return true;
|
||
}
|
||
//更新特约商户 平台证书
|
||
$this->config['certificates'] = $res;
|
||
$this->config['serial_no'] = $result['serial_no'];
|
||
$this->config['expire_time'] = $expire_time;
|
||
if(empty($applet_id)){
|
||
$config = Setting::getItem('webpay',0);
|
||
$config['wx']['certificates'] = $res;
|
||
$config['wx']['serial_no'] = $result['serial_no'];
|
||
$config['wx']['expire_time'] = $expire_time;
|
||
$model->edit('webpay',$config,0); //保存到数据库
|
||
}else{
|
||
$model->edit('wxpay',$this->config,$applet_id); //保存到数据库
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 图片上传API
|
||
*/
|
||
private function upload($file_path)
|
||
{
|
||
$file = file_get_contents($file_path);//获取网络图片
|
||
//获取文件名称
|
||
$arr = explode('/',$file_path);
|
||
$filename = $arr[sizeof($arr)-1];
|
||
$meta =[
|
||
'filename' => $filename,
|
||
'sha256' => hash('sha256',$file),
|
||
];
|
||
$url = $this->getUrl('merchant/media/upload');
|
||
$boundary = uniqid();//随机数
|
||
$headers = [
|
||
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $this->sign($url,'POST',hema_json($meta)),
|
||
'Accept:application/json',
|
||
'Content-Type:multipart/form-data;boundary=' . $boundary,
|
||
];
|
||
$params = '--' . $boundary . "\r\n";
|
||
$params .= 'Content-Disposition:form-data; name="meta"' . "\r\n";
|
||
$params .= 'Content-Type:application/json' . "\r\n\r\n";
|
||
$params .= hema_json($meta) . "\r\n";
|
||
$params .= '--' . $boundary . "\r\n";
|
||
$params .= 'Content-Disposition:form-data;name="file";filename="' . $meta['filename'] . '"' . "\r\n";
|
||
$params .= 'Content-Type:image/jpg' . "\r\n\r\n";
|
||
$params .= $file . "\r\n";
|
||
$params .= '--' . $boundary . '--' . "\r\n";
|
||
$result = json_decode(Http::post($url, $params,[],$headers),true);
|
||
if(isset($result['code'])){
|
||
$this->error = 'code:' . $result['code'] . ',msg:' . $result['message'];
|
||
return false;
|
||
}
|
||
if(isset($result['media_id'])){
|
||
return $result['media_id'];
|
||
}
|
||
$this->error = '图片上传失败';
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 调起支付签名
|
||
*/
|
||
private function paySign($data)
|
||
{
|
||
$params = $this->config['app_id'] . "\n" .
|
||
$data['timeStamp'] . "\n" .
|
||
$data['nonceStr'] . "\n" .
|
||
$data['package'] . "\n";
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商
|
||
$private_key = $this->isp_config['key_pem']; //API私有证书
|
||
}else{
|
||
//直连商户
|
||
$private_key = $this->config['key_pem']; //API私有证书
|
||
}
|
||
$raw_sign = '';
|
||
openssl_sign($params, $raw_sign, $private_key, 'sha256WithRSAEncryption');
|
||
return base64_encode($raw_sign);
|
||
}
|
||
|
||
/**
|
||
* 生成签名
|
||
* $http_method = HTTP请求的方法(GET,POST,PUT
|
||
* serial_no 为你的商户证书序列号
|
||
* $mch_private_key = 是商户API私钥,在商户平台下载的证书文件包含该文件,名称为apiclient_key.pem
|
||
* $is_isp 是否为服务商操作
|
||
*/
|
||
private function sign($url,$http_method,$body='',$is_isp=true)
|
||
{
|
||
$timestamp = time(); //时间戳
|
||
$nonce = $this->nonce(); //随机字符串
|
||
$url_parts = parse_url($url);
|
||
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
|
||
$params = $http_method . "\n" .
|
||
$canonical_url . "\n" .
|
||
$timestamp . "\n" .
|
||
$nonce . "\n" .
|
||
$body . "\n";
|
||
if($is_isp){
|
||
$mchid = $this->isp_config['mch_id']; //商户号
|
||
$serial_no = $this->isp_config['api_serial_no']; //API证书序列号
|
||
$mch_private_key = $this->isp_config['key_pem']; //API私有证书
|
||
}else{
|
||
$mchid = $this->config['mch_id']; //商户号
|
||
$serial_no = $this->config['api_serial_no']; //API证书序列号
|
||
$mch_private_key = $this->config['key_pem']; //API私有证书
|
||
}
|
||
$raw_sign = '';
|
||
openssl_sign($params, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
|
||
$sign = base64_encode($raw_sign);
|
||
//$schema = 'WECHATPAY2-SHA256-RSA2048';
|
||
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',$mchid, $nonce, $timestamp, $serial_no, $sign);
|
||
return $token;
|
||
}
|
||
/**
|
||
* 敏感信息加密
|
||
*/
|
||
private function getEncrypt($str,$is_isp = true,$applet_id='')
|
||
{
|
||
//判断平台证书是否过期
|
||
if($is_isp){
|
||
//服务商
|
||
if($this->isp_config['expire_time'] < time()){
|
||
//更新平台证书
|
||
if(!$this->certificates()){
|
||
return false;//更新失败
|
||
}
|
||
}
|
||
$public_key = $this->isp_config['certificates'];//平台证书
|
||
}else{
|
||
//直连商户
|
||
if($this->config['expire_time'] < time()){
|
||
//更新平台证书
|
||
if(!$this->certificates(false,$applet_id)){
|
||
return false;//更新失败
|
||
}
|
||
}
|
||
$public_key = $this->config['certificates'];//平台证书
|
||
}
|
||
$encrypted = '';
|
||
if (!openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
|
||
$this->error = '敏感信息加密失败';
|
||
return false;
|
||
}
|
||
return base64_encode($encrypted);//base64编码
|
||
}
|
||
/**
|
||
* 生成随机字符串
|
||
*/
|
||
private function nonce()
|
||
{
|
||
return md5(uniqid());
|
||
}
|
||
/*
|
||
* 拼接请求域名接口
|
||
*/
|
||
private function getUrl($url)
|
||
{
|
||
return $this->api_url . '/' . $this->version . '/' . $url;
|
||
}
|
||
/**
|
||
* 获取Headers数据
|
||
*/
|
||
private function getHeaders()
|
||
{
|
||
$headers = array();
|
||
foreach ($_SERVER as $key => $value) {
|
||
if (substr($key, 0, 5) === 'HTTP_') {
|
||
$key = substr($key, 5);
|
||
$key = str_replace('_', ' ', $key);
|
||
$key = str_replace(' ', '-', $key);
|
||
$key = strtolower($key);
|
||
$headers[$key] = $value;
|
||
}
|
||
}
|
||
return $headers;
|
||
}
|
||
/**
|
||
* 返回状态给微信服务器
|
||
*/
|
||
private function returnHttpCode($is_success = true, $msg = '失败')
|
||
{
|
||
$json = hema_json([
|
||
'code' => $is_success ? 'SUCCESS' : 'FAIL',
|
||
'message' => $is_success ? '成功' : $msg,
|
||
]);
|
||
if($is_success){
|
||
header('HTTP/1.1 200 OK');
|
||
}else{
|
||
header('HTTP/1.1 404 Not Found');
|
||
}
|
||
die($json);
|
||
}
|
||
/**
|
||
* 请求数据验证
|
||
**/
|
||
private function result($result)
|
||
{
|
||
if(isset($result['code'])){
|
||
$this->error = 'code:' . $result['code'] . ',msg:' . $result['message'];
|
||
return false;
|
||
}
|
||
return $result;
|
||
}
|
||
public function getError()
|
||
{
|
||
return $this->error;
|
||
}
|
||
|
||
/********** V2接口 **********/
|
||
/**
|
||
* 付款码支付
|
||
* $auth_code=付款码 $order_no=订单号 $openid=微信用户ID, $total_fee=支付金额, ,$attach=订单描述 $divide=是否分账
|
||
*/
|
||
public function micropay($auth_code,$order_no, $total_fee,$profit_sharing = false,$attach = '订单支付')
|
||
{
|
||
// 当前时间
|
||
$time = time();
|
||
// 生成随机字符串
|
||
$nonceStr = md5($time);
|
||
// API参数
|
||
$params = [
|
||
'auth_code' => $auth_code,//付款码支付
|
||
'attach' => $attach,
|
||
'nonce_str' => $nonceStr,//随机字符串
|
||
'body' => $attach,//商品描述
|
||
'out_trade_no' => $order_no,//商户订单号
|
||
'total_fee' => intval($total_fee * 100), // 价格:单位分
|
||
'spbill_create_ip' => \request()->ip(),//服务终端IP
|
||
];
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商统一下单
|
||
$values = Setting::getItem('wxpayisp',0);
|
||
$this->config['api_key'] = $this->isp_config['api_key'];//服务商商户的密钥
|
||
$params['appid'] = $this->isp_config['app_id'];//服务商商户的APPID
|
||
$params['mch_id'] = $this->isp_config['mch_id'];//服务商商户号
|
||
$params['sub_appid'] = $this->config['app_id'];//当前调起支付的小程序APPID
|
||
$params['sub_mch_id'] = $this->config['mch_id'];//服务商分配的子商户号
|
||
}else{
|
||
$params['appid'] = $this->config['app_id'];//小程序ID
|
||
$params['mch_id'] = $this->config['mch_id'];//商户号
|
||
}
|
||
//判断是否开启分账
|
||
$divide = Setting::getItem('divide',0);
|
||
if($profit_sharing or $divide['extract'] > 0){
|
||
$params['profit_sharing'] = 'Y';//开启分账
|
||
}
|
||
|
||
// 生成签名
|
||
$params['sign'] = $this->makeSign($params);
|
||
$url = 'https://api.mch.weixin.qq.com/pay/micropay';// 请求API
|
||
$result = $this->postXmlCurl($this->toXml($params), $url);
|
||
$prepay = $this->fromXml($result);
|
||
// 请求失败
|
||
if ($prepay['return_code'] === 'FAIL') {
|
||
die(hema_json(['code' => -10, 'msg' => $prepay['return_msg']]));
|
||
}
|
||
//判断付款码支付时,用户支付中,需要输入密码
|
||
if ($prepay['result_code'] === 'USERPAYING') {
|
||
return false;
|
||
}
|
||
if ($prepay['result_code'] === 'FAIL') {
|
||
die(hema_json(['code' => -10, 'msg' => $prepay['err_code_des']]));
|
||
}
|
||
return $prepay['transaction_id'];//支付交易号
|
||
}
|
||
|
||
/**
|
||
* 查询付款码支付结果是否成功
|
||
*/
|
||
public function orderquery($out_trade_no)
|
||
{
|
||
// 当前时间
|
||
$time = time();
|
||
// 生成随机字符串
|
||
$nonceStr = md5($time);
|
||
// API参数
|
||
$params = [
|
||
'out_trade_no' => $out_trade_no,
|
||
'nonce_str' => $nonceStr,//随机字符串
|
||
];
|
||
if($this->config['is_sub'] == 1){
|
||
//服务商统一下单
|
||
$this->config['api_key'] = $this->isp_config['api_key'];//服务商商户的密钥
|
||
$params['appid'] = $this->isp_config['app_id'];//服务商商户的APPID
|
||
$params['mch_id'] = $this->isp_config['mch_id'];//服务商商户号
|
||
$params['sub_appid'] = $this->config['app_id'];//当前调起支付的小程序APPID
|
||
$params['sub_mch_id'] = $this->config['mch_id'];//服务商分配的子商户号
|
||
}else{
|
||
$params['appid'] = $this->config['app_id'];//小程序ID
|
||
$params['mch_id'] = $this->config['mch_id'];//商户号
|
||
}
|
||
// 生成签名
|
||
$params['sign'] = $this->makeSign($params);
|
||
// 请求API
|
||
$url = 'https://api.mch.weixin.qq.com/pay/orderquery';
|
||
$result = $this->postXmlCurl($this->toXml($params), $url);
|
||
$prepay = $this->fromXml($result);
|
||
// 请求失败
|
||
if ($prepay['return_code'] === 'SUCCESS' AND $prepay['result_code'] === 'SUCCESS') {
|
||
return $prepay['trade_state'];
|
||
}
|
||
return 'ERROR';
|
||
}
|
||
/**
|
||
* 输出xml字符
|
||
*/
|
||
private function toXml($values)
|
||
{
|
||
if (!is_array($values)
|
||
|| count($values) <= 0
|
||
) {
|
||
return false;
|
||
}
|
||
$xml = "<xml>";
|
||
foreach ($values as $key => $val) {
|
||
if (is_numeric($val)) {
|
||
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
|
||
} else {
|
||
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
|
||
}
|
||
}
|
||
$xml .= "</xml>";
|
||
return $xml;
|
||
}
|
||
/**
|
||
* 将xml转为array
|
||
*/
|
||
private function fromXml($xml)
|
||
{
|
||
// 禁止引用外部xml实体
|
||
libxml_disable_entity_loader(true);
|
||
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
|
||
}
|
||
/**
|
||
* 以post方式提交xml到对应的接口url
|
||
*/
|
||
private function postXmlCurl($xml, $url, $cert = false, $second = 30)
|
||
{
|
||
$ch = curl_init();
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, $second);// 设置超时时间
|
||
curl_setopt($ch, CURLOPT_URL, $url);
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//https请求 不验证证书和host
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);//严格校验
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);// 要求结果为字符串且输出到屏幕上
|
||
curl_setopt($ch, CURLOPT_POST, TRUE);// post提交方式
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
|
||
curl_setopt($ch, CURLOPT_HEADER, FALSE);// 是否返回请求头
|
||
//判断是否使用证书
|
||
if($cert){
|
||
$path = root_path() . '/extend/hema/wechat/cert/';
|
||
file_put_contents($path . 'apiclient_cert.pem',$this->config['cert_pem']);
|
||
file_put_contents($path . 'apiclient_key.pem',$this->config['key_pem']);
|
||
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
|
||
curl_setopt($ch,CURLOPT_SSLCERT,$path . 'apiclient_cert.pem');
|
||
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
|
||
curl_setopt($ch,CURLOPT_SSLKEY,$path . 'apiclient_key.pem');
|
||
}
|
||
$data = curl_exec($ch);// 运行curl
|
||
curl_close($ch);
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* 生成签名MD5
|
||
*/
|
||
private function makeSign($values)
|
||
{
|
||
//签名步骤一:按字典序排序参数
|
||
ksort($values);
|
||
$string = $this->toUrlParams($values);
|
||
//签名步骤二:在string后加入KEY
|
||
$string = $string . '&key=' . $this->config['api_key'];
|
||
//签名步骤三:MD5加密
|
||
$string = md5($string);
|
||
//签名步骤四:所有字符转为大写
|
||
$result = strtoupper($string);
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 格式化参数格式化成url参数
|
||
*/
|
||
private function toUrlParams($values)
|
||
{
|
||
$buff = '';
|
||
foreach ($values as $k => $v) {
|
||
if ($k != 'sign' && $v != '' && !is_array($v)) {
|
||
$buff .= $k . '=' . $v . '&';
|
||
}
|
||
}
|
||
return trim($buff, '&');
|
||
}
|
||
} |