<?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);
}

封装微信小程序支付的更多相关文章

  1. 微信小程序支付及退款流程详解

    微信小程序的支付和退款流程 近期在做微信小程序时,涉及到了小程序的支付和退款流程,所以也大概的将这方面的东西看了一个遍,就在这篇博客里总结一下. 首先说明一下,微信小程序支付的主要逻辑集中在后端,前端 ...

  2. php对接微信小程序支付

    前言:这里我就假装你已经注册了微信小程序,并且基本的配置都已经好了.注: 个人注册小程序不支持微信支付,所以我还是假装你是企业或者个体工商户的微信小程序,其他的商户号注册,二者绑定,授权,支付开通,就 ...

  3. 微信小程序支付接入注意点

    一.微信支付后台服务器部署 服务器采用ubuntu16.04 + php7.0 + apache2.0. 微信支付后台服务使用了curl 和 samplexml ,因此php.ini配置中必须开启这两 ...

  4. 微信小程序支付开发之申请退款

    微信小程序支付跟微信公众号支付类似,这里不另做记录,如果没有开发过支付,可以查看我关于微信支付的文章 重点记录微信小程序申请退款开发过程中遇到一些坑. 退款接口比支付接口接口多了一个 双向证书 证书介 ...

  5. 微信小程序支付接入实战

    1. 微信小程序支付接入实战 1.1. 需求   最近接到一个小程序微信支付的需求,需要我写后台支持,本着能不自己写就不自己写的cv原则,在网上找到了些第三方程序,经过尝试后,最后决定了这不要脸作者的 ...

  6. Java实现微信小程序支付(完整版)

    在开发微信小程序支付的功能前,我们先熟悉下微信小程序支付的业务流程图: 不熟悉流程的建议还是仔细阅读微信官方的开发者文档. 一,准备工作 事先需要申请企业版小程序,并开通“微信支付”(即商户功能).并 ...

  7. php 微信小程序支付

    php 微信小程序支付 直接贴代码: 前端测试按钮wxml: <view class="container"> <text class="name&qu ...

  8. .Net后台实现微信小程序支付

    最近一直再研究微信支付和支付宝支付,官方支付文档中一直在讲与第三方支付打交道的原理,却没有介绍我们自己项目中的APP与后台该怎么交互(哈哈,人家也没必要介绍这一块).拜读了官方文档和前辈们的佳作,自己 ...

  9. 微信小程序支付步骤

    http://blog.csdn.net/wangsf789/article/details/53419781 最近开发微信小程序进入到支付阶段,一直以来从事App开发,所以支付流程还是熟记于心的.但 ...

随机推荐

  1. i2c调试碰到的问题

    i2c eeprom i2cget两次结果不一致 i2cset没成功. device里只看到50,却冒出了51地址. i2ctools是针对8bit地址的,而我们的eeprom都是用16bit add ...

  2. priority todo

    analyze the work about change to right spindle

  3. Prometheus监控学习笔记之PromQL操作符

    0x00 二元运算符 Prometheus 的查询语言支持基本的逻辑运算和算术运算.对于两个瞬时向量, 匹配行为可以被改变. 算术二元运算符 在 Prometheus 系统中支持下面的二元算术运算符: ...

  4. oracle 12cR2 smart flash cache实测

    最近一直在处理新系统的性能优化问题,这两天特地测试了下oracle 11gR2开始引入的smart flash cache. 其介绍参考MOS文档,How To Size the Database S ...

  5. applyColorMap()研究(如果我对现有的colormap不满意,那么如何具体来做)

    cv::applyColorMap()能够实现预定义的伪彩色,这个是众所周知的事情. 并且和matlab提供的很相近 除了这些预置的变换,如果我想实现新的变换,需要做LUT变换     cv::Mat ...

  6. linux --- 4. 虚拟环境

    一.虚拟环境的两种安装方式 1. virtualenv  虚拟环境 ①下载 virtualenv pip3 install -i https://pypi.tuna.tsinghua.edu.cn/s ...

  7. Android中activity的四个启动模式

    activity的四个启动方式分别是standard.singletop.singletask.singleinstance.第一个其实就是只要新打开活动就会新建一个实例.第二个顾名思义返回栈的顶部只 ...

  8. C语言goto语句的使用

    不使用goto语句: 使用goto语句: 使用goto语句时需要注意以下原则:1) 不要过份地使用.比如图2中的60行就没有采用goto语句跳到程序的最后面,之所以这里不使用goto是为了阅读方便.因 ...

  9. MyEclipse代码编辑器中汉字太小的解决办法(中文看不清)

    问题描述:新安装的myeclipse 2014,代码编辑器中汉字很小看不清 解决办法:调整字体即可.通过菜单Windows——Preferences,输入font过滤选择Colors and Font ...

  10. C++变量的默认初始化规则

    定义没有初始化式的变量时,系统有时候会帮我们初始化变量.系统如何初始化取决于变量的类型以及变量定义的位置. 内置类型变量是否自动初始化取决于变量定义的位置.函数体外定义的变量初始成0:函数体内定义的变 ...