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