Files
yusheng-php/application/api/controller/Payment.php

432 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\api\controller;
use think\Controller;
use think\Db;
use think\Loader;
use think\Log;
use Yzh\YunPay;
class Payment extends Controller
{
//生成订单号
private function createOrderSn() {
$orderSn = strtoupper(date('Ymd', time())).substr(time(), -5).substr(microtime(), 2, 5).sprintf('%02d',
rand(0, 99));
return $orderSn;
}
/**
* APP支付
*/
public function app_pay() {
$type = input('type', 0); //1-微信 2-支付宝 4-通联支付宝 5-通联微信 6-苹果支付
$user_id = input('user_id', 0);
$money = input('money', 0);
$coin = input('coin', 0);
$type_params = input('type_params', 0);
$type_id = input('type_id', 0);
$nobility_id = input('nobility_id', 0);//'0-购买金币充值其他是爵位id'
if($nobility_id != 0){
$nobility = model('Nobility')->buyNobilityPrice($user_id,$nobility_id);
if($nobility['code'] == 0 || $nobility['data']['price'] != $money){
return V(0, '网络错误,请重新操作!', null);
}
}
if(!$coin && $nobility_id == 0){
$coin = $money * get_system_config_value('rmb_coin_ratio');
}
if (!$user_id) {
return V(0, '请选择充值用户', null);
}
//获取用户的手机号
$user_phone = db::name('user')->where(["id" => $user_id])->value('mobile');
if(!$user_phone){
return V(0, '请先绑定手机号!', null);
}
//获取用户的实名信息
$real_name = db::name('user_auth')->where(["mobile" => $user_phone,'is_real' => 1])->find();
if(!$real_name){
return V(0, '请先实名认证', null);
}
//获取用户的年龄
if(!getAgeId($real_name['card_id'])){
return V(0, '未成年不可充值!', null);
}
if (!is_numeric($money) || floor($money) != $money || $money <= 0 || $money > 5000) {
return V(0, '请选择正确的充值金额', null);
}
$user_code = db::name('user')->where(["id" => $user_id])->value('user_code');
$title = $nobility_id == 0 ? '充值到app用户'.($user_code ?? "") ."余额!" :'购买爵位';
$order_number = $this->createOrderSn();
$data['order_sn'] = $order_number;
$data['money'] = $money;
$data['user_id'] = $user_id;
$data['pay_type'] = $type;
$data['createtime'] = time();
$data['remarke'] = $nobility_id == 0 ? '充值到app用户'.($user_code ?? "") ."余额!" :'购买爵位';
$data['type_params'] = $type_params;
$data['type_id'] = $type_id;
$data['nobility_id'] = $nobility_id;
$re = db::name('vs_user_recharge')->insert($data);
if (!$re) {
return V(0, '充值失败', null);
}
if($type == 2){
//引用支付宝sdk
Loader::import('AliPayV2.AliPay', EXTEND_PATH, '.php');
$ali = new \AliPay();
$result['ali'] = $ali->aliAppPays($order_number, $money, $title, $user_id);
}elseif($type == 1){
//引用微信sdk
Loader::import('WxPay.WxPay', EXTEND_PATH, '.php');
$wx = new \WxPay();
$result['wx'] = $wx->WxPayApp($data);
}elseif($type == 4 || $type == 5){
//引用通联sdk
Loader::import('TongLian.TongLian', EXTEND_PATH, '.php');
$tonglian = new \TongLian();
$result['tl'] = $tonglian->TongLianPay($data, $type);
}elseif($type == 6){
$result = [
'order_no' => $order_number,
// 'merchant_id' => get_system_config_value('merchant_id')
];
}else{
return V(0, '请选择正确的支付方式', null);
}
return V(1, 'app支付', $result);
}
//支付宝回调
public function notify_ali() {
//引用支付宝sdk
Loader::import('AliPayV2.AliPay', EXTEND_PATH, '.php');
$ali = new \AliPay();
$verify_result = $ali->verify($_POST);
Log::record("支付宝回调信息".json_encode($_POST),"info");
if($verify_result) {//验证成功
//商户订单号
$out_trade_no = $_POST['out_trade_no'];
//支付宝交易号
$trade_no = $_POST['trade_no'];
if ($_POST['trade_status'] == 'TRADE_SUCCESS') {
$where['order_sn']=$out_trade_no;
$where['order_type']=1;
$where['pay_type']=2;
$where['pay_status']=1;
$data=[
'trade_no'=>$trade_no
];
$res = handelCharge($where,$data);
if($res==0){
echo "fail";
return;
}
echo "success"; //请不要修改或删除
return;
}
echo "fail";
}else {
//验证失败
echo "fail";
//写入日志文件
Log::record("支付宝回调验签失败","info");
}
}
//微信回调地址 示例
public function notify_wx() {
//引用微信sdk
Loader::import('WxPay.WxPay', EXTEND_PATH, '.php');
$wx = new \WxPay();
//验证是否是微信发送且数据完整
$flag = $wx->WxPayNotifyCheck();
if ($flag['status']) {
if ($flag['data']['return_code'] == 'SUCCESS' && $flag['data']['result_code'] == 'SUCCESS') {
$out_trade_no = $flag['data']['out_trade_no'];//订单号
// $payType = $flag['data']['attach']; //商家数据包
// $time_end = strtotime($flag['data']['time_end']); //支付完成时间
$transaction_id = $flag['data']['transaction_id']; //微信支付订单号
//成功后的业务逻辑处理
$where['order_sn']=$out_trade_no;
$where['order_type']=1;//1 充值
$where['pay_type']=1;//1微信2支付宝 3通联支付宝 4通联微信
$where['pay_status']=1;
$data=[
'trade_no'=>$transaction_id
];
$res = handelCharge($where,$data);
if($res==0){
$r_arr['return_code'] = 'FAIL';
$r_arr['return_msg'] = '回调失败';
echo $wx->arrayToXml($r_arr);
die;
}
$r_arr['return_code'] = 'SUCCESS';
$r_arr['return_msg'] = '回调成功';
echo $wx->arrayToXml($r_arr);
die;
}
}
$r_arr['return_code'] = 'FAIL';
$r_arr['return_msg'] = '回调失败';
echo $wx->arrayToXml($r_arr);
die;
}
//通联支付回调地址 示例
public function allinpayNotify() {
$params = array();
foreach($_POST as $key=>$val) {//动态遍历获取所有收到的参数,此步非常关键,因为收银宝以后可能会加字段,动态获取可以兼容由于收银宝加字段而引起的签名异常
$params[$key] = $val;
}
if(count($params)<1){//如果参数为空,则不进行处理
echo "error";
exit();
}
//引用通联sdk
Loader::import('TongLian.TongLian', EXTEND_PATH, '.php');
$tonglian = new \TongLian();
$ree = $tonglian->ValidSign($params);
// Log::record("通联支付回调信息".json_encode($ree),"info");
if($tonglian->ValidSign($params)){//验签成功
Log::record("通联支付回调信息验签成功".json_encode($params),"info");
//此处进行业务逻辑处理
$pay_type =trim($params['trxreserved']);//备注信息remark
$out_trade_no = trim($params['cusorderid']); //商户订单号
$trade_no = trim($params['trxid']); // 交易号
// $gmt_payment = strtotime(trim($_POST['paytime'])); //交易付款时间
//成功后的业务逻辑处理
$where['order_sn']=$out_trade_no;
$where['order_type']=1;//1 充值
$where['pay_type']=$pay_type;//1微信2支付宝 3通联支付宝 4通联微信
$where['pay_status']=1;
$data=[
'trade_no'=>$trade_no
];
$res = handelCharge($where,$data);
if($res==0){
echo "erro";
return;
}
echo "success";
}else{
Log::record("通联支付回调信息验签失败".json_encode($params),"info");
echo "erro";
}
}
/*
* 云账户回调(提现)
*/
public function yun_callback(){
// $data = input("data", "");
// $mess = input("mess", "");
// $timestamp = input("timestamp", "");
// $sign = input("sign", "");
$data = $_POST['data']??"";
$mess = $_POST['mess']??"";
$timestamp = $_POST['timestamp']?? "";
$sign = $_POST['sign']??"";
write_log_redis("yunzhanghu_huidiao_原始数据",$_POST);
$yun_pay = new YunPay();
$result = $yun_pay->yun_callback($data,$mess,$timestamp,$sign);
if($result['code']==1){
$data = $result['data'];
if($data){
if(!isset($data['status'])){
echo 'fail';
die;
}
$status = $data['status'];
switch ($status){
case "1":
// 支付成功(对于支付宝和微信支付是最终状态,对于银行卡大部分情况是终态,小概率会出现"退汇现象",状态由"成功"变为"退汇"
db::name('vs_user_withdrawal')
->where('order_sn',$data['order_id'])
->where('status',4)
->update([
'status' => 6,
'pay_message' => $data['status_message'],
'pay_time' => time(),
'updatetime' => time()
]);
echo "success";
break;
case "2":
// 支付失败(最终状态)
// TODO 更新业务订单状态,提示用户提现失败,若有用户钱包体系,则需将提现金额退回至用户钱包
db::name('vs_user_withdrawal')
->where('order_sn',$data['order_id'])
->where('status',4)
->update([
'status' => 5,
'pay_message' => $data['status_message'],
'pay_time' => time(),
'updatetime' => time()
]);
//支付失败资金退回
$res = model('/UserWithdrawal')->withdrawal_fail($data['order_id']);
echo "success";
break;
break;
case "4":
// 订单挂单(中间状态,挂单原因会在订单详细状态信息返回)
// TODO 提示用户提现中其他逻辑如若因余额不足导致的挂单可通知财务及时充值72小时内补足余额后可自动继续支付
echo "success";
break;
case "9":
// 退汇,(最终状态,银行卡渠道特有现象,会先收到"成功"回调,然后再收到"退汇"的回调一般以成功状态间隔24小时以上
// TODO 更新业务订单状态为“退汇”(失败),通知用户提现失败,建议用户更换其他银行卡提现,若有用户钱包体系,则需将提现金额退回至用户钱包
db::name('vs_user_withdrawal')
->where('order_sn',$data['order_id'])
->where('status',4)
->update([
'status' => 5,
'pay_message' => $data['status_message'],
'pay_time' => time(),
'updatetime' => time()
]);
//支付失败资金退回
$res = model('/UserWithdrawal')->withdrawal_fail($data['order_id']);
echo "success";
break;
case "15":
// 订单取消,(最终状态,只有挂单的订单才可以取消)
// TODO 更新业务订单状态为“取消”(失败),通知用户提现失败,若有用户钱包体系,则需将提现金额退回至用户钱包
db::name('vs_user_withdrawal')
->where('order_sn',$data['order_id'])
->where('status',4)
->update([
'status' => 5,
'pay_message' => $data['status_message'],
'pay_time' => time(),
'updatetime' => time()
]);
//支付失败资金退回
$res = model('/UserWithdrawal')->withdrawal_fail($data['order_id']);
echo "success";
break;
}
}
}else{
echo "fail";
}
}
//苹果回调
public function notify_apple(){
// 1. 接收APP端参数
$orderNo = input('order_no', 0);
$paymentToken = input('payment_token', ''); // APP端获取的支付凭证
// 2. 参数校验
if (empty($orderNo) || empty($paymentToken)) {
return V(0, '参数缺失');
}
// 3. 查询订单(防止订单不存在)
$order = Db::name('vs_user_recharge')->where('order_sn', $orderNo)->find();
if (!$order) {
return V(0, '订单不存在');
}
if ($order['pay_status'] == 2) {
return V(0, '订单已支付');// 幂等处理,防止重复回调
}
// 4. 调用苹果接口验证支付凭证
$verifyResult = $this->verifyApplePayReceipt($paymentToken);
if (!$verifyResult) {
return V(0, '支付凭证验证失败');
}
// 2. 从苹果返回的凭证中解析实际支付金额
$applePayAmount = $verifyResult['receipt']['in_app'][0]['price'] ?? 0; // 苹果返回的实际支付金额
// 3. 校验金额一致性(允许微小误差,如分位四舍五入)
if (abs($order['money'] - $applePayAmount) > 0.01) {
// 金额不一致,拒绝更新订单
return V(0, '金额不一致');
}
$transaction_id = $verifyResult['receipt']['in_app'][0]['transaction_id'] ?? '';// 苹果返回的订单号
// 5. 更新订单状态
//成功后的业务逻辑处理
$where['order_sn']=$orderNo;
$where['order_type']=1;//1 充值
$where['pay_type']=6;//1微信2支付宝 3通联支付宝 4通联微信
$where['pay_status']=1;
$data=[
'trade_no' => $transaction_id
];
$res = handelCharge($where,$data);
if($res==0){
return V(0, '订单处理失败');
}
return V(1, '支付成功');
}
// 核心:调用苹果服务器验证支付凭证
private function verifyApplePayReceipt($paymentToken)
{
// 订单创建时的金额(固定/用户输入的自由金额)
// 1. 组装请求参数
$postData = json_encode([
'receipt-data' => $paymentToken, // APP端传入的支付凭
]);
// 2. 先请求生产环境,失败再试沙箱(苹果推荐逻辑)
// $url = 'https://buy.itunes.apple.com/verifyReceipt';//正式验证环境
$url = "https://sandbox.itunes.apple.com/verifyReceipt";//沙箱测试环境
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 生产环境建议开启
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$result = curl_exec($ch);
curl_close($ch);
$result = json_decode($result, true);
// 验证返回码status=0表示验证成功
if ($result['status'] == 0) {
return $result;
}
return false;
}
}