封装微信小程序支付
<?php
/**
* User: Eden
* Date: 2019/3/21
* 共有内容
*/
namespace Common\Service;
use Think\Exception;
use Vendor\Func\Http;
class WxPayService extends CommonService {
public static function unifiedOrder($openid,$order_num,$total_fee,$products_name,$notify_url = ''){
$trade_no = $order_num;
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$data = [
'appid' => C('APPID'),
'mch_id' => C('MCHID'),
'nonce_str' => self::createNonceStr(),
'sign_type' => 'MD5',
'body' => $products_name, //商品名称组合
'attach' => C('APP_NAME').'-附加信息',
'out_trade_no' => $trade_no, //订单号
'fee_type' => 'CNY',
'total_fee' => $total_fee,
'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
'goods_tag' => C('APP_NAME').'-商品标记',
'notify_url' => $notify_url ?:C('NOTIFY_URL'),
'trade_type' => 'JSAPI',
'openid' => $openid
];
$sign = self::MakeSign($data);
$data['sign'] = $sign;
$xml = self::ToXml($data);
$result = self::FromXml(Http::postXmlCurl($url,$xml));
// 加工数据
$data = [
'appId' => $result['appid'] ?: C('APPID'),
'timeStamp' => time(),
'nonceStr' => self::createNonceStr(),
'package' => 'prepay_id=' . $result['prepay_id'],
'signType' => 'MD5'
];
$sign = self::MakeSign($data);
$data['sign'] = $sign;
return $data;
}
public static function FromXml($xml)
{
if(!$xml){
throw new Exception("xml数据异常!");
}
//将XML转为array
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $values;
}
public static function ToXml($array){
if(!is_array($array)|| count($array) <= 0){
return ;
}
$xml = '<xml version="1.0">';
foreach ($array as $key=>$val){
if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
public static function createNonceStr($length = 16) {
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
$str = '';
for ( $i = 0; $i < $length; $i++ ) {
$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
public static function MakeSign($data)
{
//签名步骤一:按字典序排序参数
ksort($data);
$string = self::ToUrlParams($data);
//签名步骤二:在string后加入KEY
$string = $string . "&key=".C('WEIXIN_PAY_KEY');
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
public static function ToUrlParams($array)
{
$buff = "";
foreach ($array as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
}
/***
* 发起捐赠
*/
public function pay()
{
if (!$openid = trim($_POST['openid'])) {
$this->json->setErr(10001, '缺少参数');
$this->json->Send();
}
if (!$donate_id = $_POST['donate_id']) {
$this->json->setErr(10001, '缺少参数donate_id');
$this->json->Send();
}
$amount = (float)$_POST['amount']; // 实际支付金额
if ($amount <= 0) {
$this->json->setErr(10002, '支付金额不可为0或负数');
$this->json->Send();
}
$donate = M('donate');
$donate_info = $donate->where(array('id' => $donate_id, 'status' => 1, 'is_show' => 1))->find();
if (!$donate_info) {
$this->json->setErr(10002, '捐赠内容不存在');
$this->json->Send();
}
// 判断当前是否可以捐赠
$now = time();
if ($now < (int)$donate_info['start_time']) {
$this->json->setErr(10003, '捐赠未开始');
$this->json->Send();
}
if ($now > (int)$donate_info['end_time']) {
$this->json->setErr(10004, '捐赠已结束');
$this->json->Send();
}
$money = $donate_info['money']; // 已有捐赠金额
$user = M('user');
$user_info = $user->where(array('openid' => $openid))->find();
if (!$user_info) {
$this->json->setErr(10001, '用户信息不存在');
$this->json->Send();
}
$uid = $user_info['id'];
// step1 生成订单
$order_info = $this->makeOrder($uid, $donate_id, $amount, $money);
$order_num = $order_info['order_num'];
$products_name = $order_info['products_name'];
// step2 unifiedOrder
$unifiedorder = WxPayService::unifiedOrder($openid, $order_num, $amount * 100, $products_name);
// step3 将数据package下放到小程序中
$this->json->setAttr('data', $unifiedorder);
$this->json->Send();
}
/***
* 生成捐赠订单
* @param $uid
* @param $donate_id
* @param $amount
* @param $money
* @return mixed
*/
private function makeOrder($uid, $donate_id, $amount, $money)
{
$donate_order = M('donate_order');
$now = time();
$order_num = 'do' . $uid . substr($now, 3) . rand(1000, 9999);
$order_add_data = [
'donate_id' => $donate_id,
'order_num' => $order_num,
'amount' => $amount, //订单价格
'money' => $money + $amount,
'uid' => $uid,
'status' => 1, //未到账
'create_time' => $now, //订单生成时间
];
$order_add_flag = $donate_order->add($order_add_data);
if (!$order_add_flag) {
$this->json->setErr(10003, '生成订单失败');
$this->json->Send();
}
$return_data['order_num'] = $order_num;
$return_data['products_name'] = '捐赠';
return $return_data;
}
//微信支付回调
public function order_notice()
{
$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
$data = WxPayService::FromXml($xml);
// 保存微信服务器返回的签名sign
$data_sign = $data['sign'];
// sign不参与签名算法
unset($data['sign']);
$sign = WxPayService::MakeSign($data);
Clog::setLog($data, 'order_notice 回调数据');
// 判断签名是否正确 判断支付状态
if (($sign === $data_sign) && ($data['return_code'] == 'SUCCESS') && ($data['result_code'] == 'SUCCESS')) {
//获取服务器返回的数据
$order_num = $data['out_trade_no']; //订单单号
$openid = $data['openid']; //付款人openID
$total_fee = $data['total_fee']; //付款金额
$transaction_id = $data['transaction_id']; //微信支付流水号
$user = M('user');
$user_info = $user->where(array('openid' => $openid))->find();
$total_payed_price = $total_fee / 100;
$save_data = array(
'total_payed_price' => $total_payed_price, //实际到帐金额
'transaction_id' => $transaction_id,
'pay_time' => time(),
'status' => 2,
);
// 开启事务
M()->startTrans();
$error_count = 0;
// step 1 修改充值订单数据
$donate_order = M('donate_order');
$donate_order_info = $donate_order->where(array('order_num' => $order_num, 'uid' => $user_info['id']))->find();
$donate_amount = $donate_order_info['amount'];
$save_flag = $donate_order->where(array('order_num' => $order_num, 'uid' => $user_info['id']))->save($save_data);
if (!$save_flag) {
$error_count++;
Clog::setLog('修改订单失败', 'order_notice 订单数据');
}
// step 2 修改捐赠总数
$donate = M('donate');
$donate_info = $donate->where(['id' => $donate_order_info['donate_id']])->find();
$save_donate_data = [
'money' => $donate_info['money'] + $donate_amount
];
$money_save_flag = $donate->where(['id' => $donate_order_info['donate_id']])->save($save_donate_data);
if (!$money_save_flag) {
$error_count++;
Clog::setLog('修改捐赠总数失败', 'order_notice 捐款数据');
}
// step 3 处理configs中的统计信息
$configService = new ConfigService();
$key = ['key' => ['in', ['total_donate', 'total_help', 'total_join']]];
$total_data = $configService->queryKey($key);
$edit_donate = $configService->updateOneKey('total_donate',$total_data['total_donate']+$total_payed_price);
if (!$edit_donate && $edit_donate !== 0) {
$error_count ++;
}
if ((float)$donate_info['money'] === 0.00) {
// 第一次被帮助
$edit_help = $configService->updateOneKey('total_help',$total_data['total_help']+1);
if (!$edit_help && $edit_help !== 0) {
$error_count ++;
}
}
$save_join = $configService->updateOneKey('total_join',$total_data['total_join']+1);
if (!$save_join && $save_join !== 0) {
$error_count ++;
}
if ($error_count > 0) {
Clog::setLog('回滚了', 'order_notice');
M()->rollback();
$result = -2;
} else {
Clog::setLog('commit了', 'order_notice');
M()->commit();
$result = 0;
}
} else {
Clog::setLog('签名有误', 'order_notice');
$result = -1;
}
Clog::setLog($result, 'order_notice');
// 返回状态给微信服务器
$str = '';
if ($result === 0) { // 成功之后不会再回调
$str = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
} elseif ($result === -1) { // 失败后会继续发送几次回调
$str = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
} elseif ($result === -2) { // 失败后会继续发送几次回调
$str = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[操作失败]]></return_msg></xml>';
}
exit($str);
}
封装微信小程序支付的更多相关文章
- 微信小程序支付及退款流程详解
微信小程序的支付和退款流程 近期在做微信小程序时,涉及到了小程序的支付和退款流程,所以也大概的将这方面的东西看了一个遍,就在这篇博客里总结一下. 首先说明一下,微信小程序支付的主要逻辑集中在后端,前端 ...
- php对接微信小程序支付
前言:这里我就假装你已经注册了微信小程序,并且基本的配置都已经好了.注: 个人注册小程序不支持微信支付,所以我还是假装你是企业或者个体工商户的微信小程序,其他的商户号注册,二者绑定,授权,支付开通,就 ...
- 微信小程序支付接入注意点
一.微信支付后台服务器部署 服务器采用ubuntu16.04 + php7.0 + apache2.0. 微信支付后台服务使用了curl 和 samplexml ,因此php.ini配置中必须开启这两 ...
- 微信小程序支付开发之申请退款
微信小程序支付跟微信公众号支付类似,这里不另做记录,如果没有开发过支付,可以查看我关于微信支付的文章 重点记录微信小程序申请退款开发过程中遇到一些坑. 退款接口比支付接口接口多了一个 双向证书 证书介 ...
- 微信小程序支付接入实战
1. 微信小程序支付接入实战 1.1. 需求 最近接到一个小程序微信支付的需求,需要我写后台支持,本着能不自己写就不自己写的cv原则,在网上找到了些第三方程序,经过尝试后,最后决定了这不要脸作者的 ...
- Java实现微信小程序支付(完整版)
在开发微信小程序支付的功能前,我们先熟悉下微信小程序支付的业务流程图: 不熟悉流程的建议还是仔细阅读微信官方的开发者文档. 一,准备工作 事先需要申请企业版小程序,并开通“微信支付”(即商户功能).并 ...
- php 微信小程序支付
php 微信小程序支付 直接贴代码: 前端测试按钮wxml: <view class="container"> <text class="name&qu ...
- .Net后台实现微信小程序支付
最近一直再研究微信支付和支付宝支付,官方支付文档中一直在讲与第三方支付打交道的原理,却没有介绍我们自己项目中的APP与后台该怎么交互(哈哈,人家也没必要介绍这一块).拜读了官方文档和前辈们的佳作,自己 ...
- 微信小程序支付步骤
http://blog.csdn.net/wangsf789/article/details/53419781 最近开发微信小程序进入到支付阶段,一直以来从事App开发,所以支付流程还是熟记于心的.但 ...
随机推荐
- i2c调试碰到的问题
i2c eeprom i2cget两次结果不一致 i2cset没成功. device里只看到50,却冒出了51地址. i2ctools是针对8bit地址的,而我们的eeprom都是用16bit add ...
- priority todo
analyze the work about change to right spindle
- Prometheus监控学习笔记之PromQL操作符
0x00 二元运算符 Prometheus 的查询语言支持基本的逻辑运算和算术运算.对于两个瞬时向量, 匹配行为可以被改变. 算术二元运算符 在 Prometheus 系统中支持下面的二元算术运算符: ...
- oracle 12cR2 smart flash cache实测
最近一直在处理新系统的性能优化问题,这两天特地测试了下oracle 11gR2开始引入的smart flash cache. 其介绍参考MOS文档,How To Size the Database S ...
- applyColorMap()研究(如果我对现有的colormap不满意,那么如何具体来做)
cv::applyColorMap()能够实现预定义的伪彩色,这个是众所周知的事情. 并且和matlab提供的很相近 除了这些预置的变换,如果我想实现新的变换,需要做LUT变换 cv::Mat ...
- linux --- 4. 虚拟环境
一.虚拟环境的两种安装方式 1. virtualenv 虚拟环境 ①下载 virtualenv pip3 install -i https://pypi.tuna.tsinghua.edu.cn/s ...
- Android中activity的四个启动模式
activity的四个启动方式分别是standard.singletop.singletask.singleinstance.第一个其实就是只要新打开活动就会新建一个实例.第二个顾名思义返回栈的顶部只 ...
- C语言goto语句的使用
不使用goto语句: 使用goto语句: 使用goto语句时需要注意以下原则:1) 不要过份地使用.比如图2中的60行就没有采用goto语句跳到程序的最后面,之所以这里不使用goto是为了阅读方便.因 ...
- MyEclipse代码编辑器中汉字太小的解决办法(中文看不清)
问题描述:新安装的myeclipse 2014,代码编辑器中汉字很小看不清 解决办法:调整字体即可.通过菜单Windows——Preferences,输入font过滤选择Colors and Font ...
- C++变量的默认初始化规则
定义没有初始化式的变量时,系统有时候会帮我们初始化变量.系统如何初始化取决于变量的类型以及变量定义的位置. 内置类型变量是否自动初始化取决于变量定义的位置.函数体外定义的变量初始成0:函数体内定义的变 ...