Thinkphp6在线预约按摩系统H5对接杉德宝支付开发 第三方支付平台

  • Post author:
  • Post category:php


在线预约按摩系统后端使用的是thinkphp6开发的 前端是使用uniapp开发的,在微信浏览器里面一打开就会自动授权登录

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1、在\app\common.php底部增加一个打印测试使用的

if (!function_exists('ljLog')) {
    function ljLog($data, $logName='DEBUG', $fname="testlog"){
        // file_put_contents("testlog", "[".date('Y-m-d H:i:s')."] ".$logName.":" . json_encode($data,JSON_UNESCAPED_UNICODE) . "\r\n", FILE_APPEND);
        file_put_contents($fname, "[".date('Y-m-d H:i:s')."] ".$logName.":" . var_export($data,true) . "\r\n\r\n", FILE_APPEND);
    }
}

2、\app\shop\route\route.php 底部增加

//支付
Route::any('IndexSandPay/returnPay', 'IndexSandPay/returnPay');

3、增加 \app\shop\controller\IndexSandPay.php

<?php
namespace app\shop\controller;

use app\ApiRest;

use think\App;
use think\facade\Db;


class IndexSandPay extends ApiRest
{

    // 支付接口地址
    // private $apiUrl = 'https://sandcash-uat01.sand.com.cn'; //测试
    private $apiUrl = 'https://sandcash.mixienet.com.cn';
    
    // 退款接口地址
    // private $refundApiUrl = 'https://smp-uat01.sand.com.cn/gw/api/order/refund'; //测试
    private $refundApiUrl = 'https://cashier.sandpay.com.cn/gw/api/order/refund';
    
    // 代付接口地址
    // private $dfApiUrl = 'https://dsfp-uat01.sand.com.cn/agent-main/openapi'; //测试
    private $dfApiUrl = 'https://caspay.sandpay.com.cn/agent-main/openapi';
    
    // 商户号
    // private $mer_no = "68888TS121355"; //测试
    private $mer_no = "6888803121355";
    
    // 公钥文件
    // private $publicKeyPath = "sandcert/sand-test-test.cer"; //测试
    private $publicKeyPath = "sandcert/sand.cer";
    // 私钥文件
    // private $privateKeyPath = "sandcert/sand_test.pfx"; //测试
    private $privateKeyPath = "sandcert/sand456.pfx";
    
    // 私钥证书密码
    private $privateKeyPwd = "123456";

    public function __construct ( App $app = null)
    {
    }


    /**
     * @param $paymentApp
     * @param $openid
     * @param $uniacid
     * @param $body
     * @param $attach
     * @param $totalprice
     * @throws \WxPayException
     * 支付
     */
    public function createWeixinPay($paymentApp, $openid, $uniacid, $body, $attach, $totalprice, $return_url=''){
        // 支付扩展域
        $pay_extra["mer_app_id"]    = $paymentApp['app_id']; //公众号mer_app_id
        $pay_extra["openid"]        = $openid; //使用微信公众号的mer_app_id获取每个用户的openid

        // 终端/网站参数
        $meta_option[]  = ["s"=>"Android","n"=>"","id"=>"","sc"=>""];
        $meta_option[]  = ["s"=>"IOS","n"=>"","id"=>"","sc"=>""];
        
        // ljLog(compact('paymentApp','openid','uniacid','body','attach','totalprice'), 'createWeixinPay');

        $data = [
            "version"       => "10",
            "mer_no"        => $this->mer_no,
            "mer_order_no"  => $attach['out_trade_no'],
            "create_time"   => date('YmdHis',time()),
            "order_amt"     => $totalprice, //例:"order_amt"="0.11"    单位: 元
            "notify_url"    => "https://".$_SERVER['HTTP_HOST']."/index.php/shop/IndexSandPay/returnPay",
            "return_url"    => $return_url,
            "create_ip"     => str_ireplace(".", "_", $_SERVER['REMOTE_ADDR']),
            "pay_extra"     => json_encode($pay_extra), //支付扩展域
            "accsplit_flag" => "NO",
            "sign_type"     => "RSA",
            "store_id"      => "000000",
            "expire_time"   => date('YmdHis',strtotime('+1 hour')),
            "goods_name"    => $body, //商品名称
            "product_code"  => "02010002", //产品编码
            "clear_cycle"   => "3", //3-D1;0-T1;1-T0;2-D0
            "jump_scheme"   => "vcannft://scpay",
            "meta_option"   => json_encode($meta_option), //终端/网站参数
            "extend"        => json_encode($attach), //扩展域
        ];
        // step2: 使用私钥签名报文
        $priKey = $this->loadPk12Cert($this->privateKeyPath, $this->privateKeyPwd);
        // $data['sign'] = $this->sign($this->getSignStr($data), $priKey);
        $data['sign'] = $this->sign($this->getSignStr($data), $priKey);

        ljLog($data, 'createWeixinPay data');

        $query = http_build_query($data);
        $payurl = $this->apiUrl."/pay/h5/wechatpay?".$query;
        
        ljLog($payurl, 'URL链接');
        ljLog($attach['out_trade_no'], '商户订单号');
        ljLog($data['create_time'], '请求时间');
        return $payurl;






        global $_GPC, $_W;
        $setting['mini_appid']         = $paymentApp['app_id'];
        $setting['mini_appsecrept']    = $paymentApp['secret'];
        $setting['mini_mid']           = $paymentApp['payment']['merchant_id'];
        $setting['mini_apicode']       = $paymentApp['payment']['key'];
        $setting['apiclient_cert']     = $paymentApp['payment']['cert_path'];
        $setting['apiclient_cert_key'] = $paymentApp['payment']['key_path'];
        define('WX_APPID', $setting['mini_appid']);
        define('WX_MCHID', $setting['mini_mid']);
        define('WX_KEY', $setting['mini_apicode']);
        define('WX_APPSECRET', $setting['mini_appsecrept']);
        define('WX_SSLCERT_PATH', $setting['apiclient_cert']);
        define('WX_SSLKEY_PATH', $setting['apiclient_cert_key']);
        define('WX_CURL_PROXY_HOST', '0.0.0.0');
        define('WX_CURL_PROXY_PORT', 0);
        define('WX_REPORT_LEVENL', 0);


        require_once PAY_PATH . "/weixinpay/lib/WxPay.Api.php";
        require_once PAY_PATH . "/weixinpay/example/WxPay.JsApiPay.php";

        $tools = new \JsApiPay();
        $input = new \WxPayUnifiedOrder();

        $input->SetBody($body);
        $input->SetAttach(json_encode($attach));
        $input->SetOut_trade_no($attach['out_trade_no']);
        $input->SetTotal_fee($totalprice *100);
        $input->SetTime_start(date("YmdHis"));

        $param_arr=[
            'i' => $uniacid,
            't' => $_GPC['t'],
            'v' => $_GPC['v'],
            'is_app' => $paymentApp['is_app'],
            'n' => APP_MODEL_NAME,

        ];
        $reply_path=json_encode($param_arr);
        //需要判断 是否是微擎的版本
        if(defined('IS_WEIQIN')){
            $path  = "https://" . $_SERVER['HTTP_HOST'] ."/addons/".APP_MODEL_NAME."/core2/app/Common/wexinPay.php?params=".$reply_path;
            $paths = "https://" . $_SERVER['HTTP_HOST'] ."/addons/".APP_MODEL_NAME."/core2/app/Common/wexinPay.php?ck=789";
            $a     = @file_get_contents($paths);
            if($a != 1){
                $this->errorMsg('发起支付失败');
            }
        }else{
            $path  = "https://" . $_SERVER['HTTP_HOST'] ."/wexinPay.php?params=".$reply_path;
            $paths = "https://" . $_SERVER['HTTP_HOST'] ."/wexinPay.php?ck=789";
            $a     = @file_get_contents($paths);
            if($a != 1){
                $this->errorMsg('发起支付失败');
            }

        }

        $this ->lb_logOutput('BaseApiPath:-----'.$path);

        $input->SetNotify_url($path);

        if($paymentApp['is_app']!=1){

            $input->SetTrade_type("JSAPI");

            $input->SetOpenid($openid);

        }else{

            $input->SetTrade_type("APP");

        }

        $order = \WxPayApi::unifiedOrder($input);

        if(!empty($order['return_code'])&&$order['return_code'] == 'FAIL'){

            $this->errorMsg($order['return_msg']);
        }

        $order['mini_mid'] = $setting['mini_mid'];

        if($paymentApp['is_app']!=1){

            $jsApiParameters = $tools->GetJsApiParameters($order);

            $jsApiParameters = json_decode($jsApiParameters, true) ;

        } else{

            $jsApiParameters = $this->getOrder($order);
        }

        if (!empty($jsApiParameters['return_code']))
             $this->errorMsg( '发起支付失败');

        return $jsApiParameters;
    }

    /**
     * 支付回调
     */
    public function returnPay(){
        ljLog('**',"in--sandNotify");
        $this->lb_logOutput("in--sandNotify");
        //支付数据
        $sign = $_POST['sign'] ?? ''; //签名
        // $data = stripslashes($_POST['data'] ?? ''); //支付数据
        $data = $_POST['data'] ?? ''; //支付数据
        // ljLog($data, 'xmlData in sand:-----');
        $this->lb_logOutput('xmlData in sand:-----'.$data);
        // ljLog($sign, 'xmlData in sand sign:-----');
        $this->lb_logOutput('xmlData in sand sign:-----'.$sign);
        
        // $data = '{"head":{"version":"1.0","respTime":"20230810190914","respCode":"000000","respMsg":"成功"},"body":{"mid":"6888803121355","orderCode":"20230810190903024400000244","tradeNo":"20230810190903024400000244","clearDate":"20230810","totalAmount":"000000000001","orderStatus":"1","payTime":"20230810190914","settleAmount":"000000000001","buyerPayAmount":"000000000001","discAmount":"000000000000","txnCompleteTime":"20230810190911","payOrderCode":"20230810001268010000000000057674","accLogonNo":"ogI_86v3QDXmfeUp3jwr0OS3fEts","accNo":"","midFee":"000000000000","extraFee":"000000000000","specialFee":"000000000000","plMidFee":"000000000000","bankserial":"4200001943202308102035947438","externalProductCode":"00002020","cardNo":"","creditFlag":"","bid":"","benefitAmount":"000000000000","remittanceCode":"","extend":"{\\"type\\":\\"Massage\\",\\"out_trade_no\\":\\"20230810190903024400000244\\"}"}}';
        // $sign = 'h9SfF1aQYRsBiLctZcMa8oX9Ogq/5umE0+Uf0ssT8F3ofQsfbVJP7oXANBbp5ld+uUboOpF714BzmlI7Hywhwv+Xn3Sot8GtAg2QRKXzH8y1NSmrgFBIrcHdpE2LPJ66Xul2/gZGRDNRSDIoz67t5yEPifihj8gRRNW9bK/lEoxnmj7ToIem3G5/Cc1kqZohtdY0zMhvLcE6Y7DOP5scHaHgvSp9wLcP05rchPEE7w8tGOgTk0FlewpQts6Wo1oW5z7ruVRqcKsNIRkSObjFngHVDxI9vUWHnlWeWPSdQymPjlj0Jj830jGWqe/MXAIp/JOJTOsyiy66SFzQL8tWCw==';
        
        //验签
        $pubKey = $this->loadX509Cert($this->publicKeyPath);
        $verifyResult = $this->verify2($data, $sign, $pubKey);
        //halt($verifyResult);
        if($verifyResult){
            $result = json_decode($data, true);
            // dump($result);exit;
            $head   = $result['head'];
            $body   = $result['body'];
            dump($head);
            dump($body);
            if ($head['respCode'] === '000000' && $head['respMsg'] === '成功') {
                $extend = isset($body['extend']) && $body['extend'] ? json_decode($body['extend'], true) : [];
                if(isset($extend['type']) && $extend['type']){
                    $type       = $extend['type'];
                    $orderCode  = $body['orderCode'];
                    
                    dump($type);
                    dump($orderCode);
                    
                    if($type == 'Balance'){
                        $order_model = new \app\massage\model\BalanceOrder();
                        $res         = $order_model->orderResult($orderCode, $body['payOrderCode'] ?? $orderCode);
                        
                        if($res){
                            return "respCode=000000";
                        }else{
                            return "FAIL";
                        }
                    }elseif($type == 'Massage'){
                        $order_model = new \app\massage\model\Order();
                        $res         = $order_model->orderResult($orderCode, $body['payOrderCode'] ?? $orderCode);
                        
                        if($res){
                            return "respCode=000000";
                        }else{
                            return "FAIL";
                        }
                    }elseif($type == 'Mall'){
                        $order_model = new \app\massage\model\CoachOrder();
                        $res         = $order_model->orderResult($orderCode, $body['payOrderCode'] ?? $orderCode);
                        
                        if($res){
                            return "respCode=000000";
                        }else{
                            return "FAIL";
                        }
                    }
                }
            }
        }
        
        return "FAIL";
    }

    // 退款
    public function orderRefundApi($paymentApp, $total_fee, $refund_fee, $order_code, $ori_order_code){
        $head['version'] = "1.0"; //* 版本号
        $head['method'] = "sandpay.trade.refund"; //* 接口名称
        $head['productId'] = "00002020"; //* 产品编码  云账户合作电子支付户余额支付:00002046  云账户合作电子宝易付支付:00002047  云账户组合支付:00002048
        $head['accessType'] = 1; //* 接入类型 1-普通商户接入 2-平台商户接入
        $head['mid'] = $this->mer_no; //* 商户ID 收款方商户号
        // $head['plMid'] = ; //平台ID  接入类型为2时必填,在担保支付模式下填写核心商户号  在杉德宝平台终端模式下填写平台商户号
        $head['channelType'] = "08"; //* 渠道类型  商户的真实应用场景,可选项包括:07-互联网 08-移动端
        $head['reqTime'] = date('YmdHis',time()); //* 请求时间  格式:yyyyMMddHHmmss
        
        $body = [
            'orderCode' => $order_code, //* 商户订单号 指发起交易的流水号,建议订单号有日期
            'oriOrderCode' => $ori_order_code, //* 原商户订单号 待退款的商户订单号
            'refundAmount' => str_pad(intval($refund_fee * 100),12,0,STR_PAD_LEFT), //* 退款金额 例 000000000101 代表 1.01 元
            // 'refundByfAmt' => , //退宝易付金额 如产品编码为00002047,则必填
            // 'refundBonus' => , //退奖励金金额
            // 'refundAccountAmt' => , //退账户金额 如产品编码为00002046,则必填
            'notifyUrl' => "https://".$_SERVER['HTTP_HOST']."/index.php/shop/IndexSandPay/returnRefund", //* 异步通知地址 杉德支付主动通知商户退款申请结果的http/https路径
            // 'refundReason' => , //退款原因 描述退款申请原因
            // 'extend' => , //扩展域 如上送,在异步通知和查询接口中将返回相同的值
        ];
        $data = [ 'head' => $head, 'body' => $body ];
        
        // step2: 生成AESKey并使用公钥加密
        $AESKey = $this->aes_generate(16); 
        $pubKey = $this->loadX509Cert($this->publicKeyPath);
        $priKey = $this->loadPk12Cert($this->privateKeyPath, $this->privateKeyPwd);
        $encryptKey = $this->RSAEncryptByPub($AESKey, $pubKey);
        
        // step3: 使用AESKey加密报文
        $encryptData = $this->AESEncrypt($data, $AESKey);
        
        // step4: 使用私钥签名报文
        $sign = $this->sign($data, $priKey);
        
        // step5: 拼接post数据
        $post['charset'] = "utf-8"; //* 编码方式  默认utf-8
        $post['data'] = json_encode($data); //* 交易报文  包含head(公共报文)和body(接口请求报文)
        $post['signType'] = "01"; //* 签名类型 默认01,表示采用SHA1+RSA算法
        $post['sign'] = $sign; //* 签名 对data进行签名,签名结果采用base64编码
        // $post['extend'] = ; //扩展域

        $url = $this->refundApiUrl;
        // $ret = $this->http_post_json($url, $post);
        // ljLog($ret, 'orderRefundApi $ret');
        $ret = 'charset%3DUTF-8%26signType%3D01%26sign%3DV8iTQRbbnhNNGLQGXSZwrktm%2BKqedJWrN%2FFJtnl3vLnIoztPv6UZ0onjoFwTQDvTtrdJm0rhvzirbX%2FZRe%2FAFHp6d%2FG%2F6HG%2BtKZhoyuKKRpjV84crUmunADlEL1M0MCidOntuEDWk0idqSsFT4oisgn61GYVRPLGe9UgvqIbtg9K7FUf2N9GQ9GUc1tEq%2FrYfEcqfcq%2Fzt0FsObHjSftZyUH2c2AjokjHqrYOVgPGghBsgPKg8AUcRLNcNcfn7gSyBlzV5Jcuuibz7rofOZDBOr%2FZnON1Hu%2FVSj7BbQdGk4p1UJGrIpSgrdd1DK2icthN3MXjrKnKR%2Fn2u3pHOtYiA%3D%3D%26data%3D%7B%22head%22%3A%7B%22respTime%22%3A%2220230811152809%22%2C%22respMsg%22%3A%22%E4%BD%99%E9%A2%9D%E4%B8%8D%E8%B6%B3%2F%E8%B6%85%E9%99%90%22%2C%22version%22%3A%221.0%22%2C%22respCode%22%3A%22040004%22%7D%2C%22body%22%3A%7B%7D%7D';
        $ret = rawurldecode($ret);
        $ret = str_replace('+', '%2B', $ret);
        parse_str($ret, $arr);
        
        try {
            // step7: 使用公钥验签报文
            $this->verify2(($arr['data']), $arr['sign'], $pubKey);
            return  [
                'code'      => 200,
                'verify'    => $arr['data'] ?? '',
                'jjson'     => $data,
                'vjson'     => json_encode($post),
                'json'      => json_encode($arr),
                'url'       => 'https://open.sandpay.com.cn/product/detail/43324/43895/',
            ];
        } catch (\Exception $e) {
            ljLog([
                'code'          => 400,
                'err_code_des'  =>  $e->getMessage(),
                'url'           => 'https://open.sandpay.com.cn/product/detail/43324/43895/',
            ], 'orderRefundApi err');
            return [
                'code'          => 400,
                'err_code_des'  =>  $e->getMessage(),
                'url'           => 'https://open.sandpay.com.cn/product/detail/43324/43895/',
            ];
            echo $e->getMessage();
            exit;
        }
    }

    /**
     * 退款回调
     */
    public function returnRefund(){
        ljLog('**',"in--sandRefundNotify");
        $this->lb_logOutput("in--sandRefundNotify");
        //支付数据
        $sign = $_POST['sign'] ?? ''; //签名
        // $data = stripslashes($_POST['data'] ?? ''); //支付数据
        $data = $_POST['data'] ?? ''; //支付数据
        ljLog($data, 'xmlData in sandRefund:-----');
        // $this->lb_logOutput('xmlData in sand:-----'.$data);
        ljLog($sign, 'xmlData in sandRefund sign:-----');
        // $this->lb_logOutput('xmlData in sand sign:-----'.$sign);
        
        exit;
        
        // $data = '{"head":{"version":"1.0","respTime":"20230810190914","respCode":"000000","respMsg":"成功"},"body":{"mid":"6888803121355","orderCode":"20230810190903024400000244","tradeNo":"20230810190903024400000244","clearDate":"20230810","totalAmount":"000000000001","orderStatus":"1","payTime":"20230810190914","settleAmount":"000000000001","buyerPayAmount":"000000000001","discAmount":"000000000000","txnCompleteTime":"20230810190911","payOrderCode":"20230810001268010000000000057674","accLogonNo":"ogI_86v3QDXmfeUp3jwr0OS3fEts","accNo":"","midFee":"000000000000","extraFee":"000000000000","specialFee":"000000000000","plMidFee":"000000000000","bankserial":"4200001943202308102035947438","externalProductCode":"00002020","cardNo":"","creditFlag":"","bid":"","benefitAmount":"000000000000","remittanceCode":"","extend":"{\\"type\\":\\"Massage\\",\\"out_trade_no\\":\\"20230810190903024400000244\\"}"}}';
        // $sign = 'h9SfF1aQYRsBiLctZcMa8oX9Ogq/5umE0+Uf0ssT8F3ofQsfbVJP7oXANBbp5ld+uUboOpF714BzmlI7Hywhwv+Xn3Sot8GtAg2QRKXzH8y1NSmrgFBIrcHdpE2LPJ66Xul2/gZGRDNRSDIoz67t5yEPifihj8gRRNW9bK/lEoxnmj7ToIem3G5/Cc1kqZohtdY0zMhvLcE6Y7DOP5scHaHgvSp9wLcP05rchPEE7w8tGOgTk0FlewpQts6Wo1oW5z7ruVRqcKsNIRkSObjFngHVDxI9vUWHnlWeWPSdQymPjlj0Jj830jGWqe/MXAIp/JOJTOsyiy66SFzQL8tWCw==';
        
        //验签
        $pubKey = $this->loadX509Cert($this->publicKeyPath);
        $verifyResult = $this->verify2($data, $sign, $pubKey);
        //halt($verifyResult);
        if($verifyResult){
            $result = json_decode($data, true);
            // dump($result);exit;
            $head   = $result['head'];
            $body   = $result['body'];
            dump($head);
            dump($body);
            if ($head['respCode'] === '000000' && $head['respMsg'] === '成功') {
                $extend = isset($body['extend']) && $body['extend'] ? json_decode($body['extend'], true) : [];
                if(isset($extend['type']) && $extend['type']){
                    $type       = $extend['type'];
                    $orderCode  = $body['orderCode'];
                    
                    dump($type);
                    dump($orderCode);
                    
                    if($type == 'Balance'){
                        $order_model = new \app\massage\model\BalanceOrder();
                        $res         = $order_model->orderResult($orderCode, $body['payOrderCode'] ?? $orderCode);
                        
                        if($res){
                            return "respCode=000000";
                        }else{
                            return "FAIL";
                        }
                    }elseif($type == 'Massage'){
                        $order_model = new \app\massage\model\Order();
                        $res         = $order_model->orderResult($orderCode, $body['payOrderCode'] ?? $orderCode);
                        
                        if($res){
                            return "respCode=000000";
                        }else{
                            return "FAIL";
                        }
                    }elseif($type == 'Mall'){
                        $order_model = new \app\massage\model\CoachOrder();
                        $res         = $order_model->orderResult($orderCode, $body['payOrderCode'] ?? $orderCode);
                        
                        if($res){
                            return "respCode=000000";
                        }else{
                            return "FAIL";
                        }
                    }
                }
            }
        }
        
        return "FAIL";
    }

    /**
     * 实时付款
     */
    public function agentpay($orderCode, $tranAmt, $accNo, $accName){
        $data = [
            "version"       => "01",
            "productId"     => "00000004", //付款对私:00000004 付款对公:00000003
            "tranTime"      => date('YmdHis'), //格式:yyyyMMddHHmmss
            "orderCode"     => $orderCode, //订单号  AN12..30
            "tranAmt"       => str_pad(intval($tranAmt * 100),12,0,STR_PAD_LEFT), //金额,精确到分,不足12位前面补0
            "currencyCode"  => 156,
            "accAttr"       => 0, //0-对私 1-对公  注:accAttr选择对私时,accType选银行卡
            "accType"       => 4, //3-公司账户 4-银行卡  注:accAttr选择对公时,accType选公司账户
            "accNo"         => $accNo, //收款人账户号
            "accName"       => $accName, //收款人账户名
            // "provNo"        => , //收款人开户省份编码
            // "cityNo"        => , //收款人开户城市编码
            // "bankName"      => , //收款账户开户行名称
            // "bankType"      => , //收款人账户联行号
            "remark"        => '提现', //摘要
            "payMode"       => 1, //付款模式
            "channelType"   => "07", //渠道类型
            // "extendParams"  => , //业务扩展参数
            // "reqReserved"   => , //请求方保留域  如需发送交易结果至收款方,则必填,值为收款方的短信通知内容
            // "extend"        => , //extend
            // "phone"         => , //手机号  如需发送交易结果至收款方,则必填
        ];
        
        // step2: 生成AESKey并使用公钥加密
        $AESKey = $this->aes_generate(16); 
        $pubKey = $this->loadX509Cert($this->publicKeyPath);
        $priKey = $this->loadPk12Cert($this->privateKeyPath, $this->privateKeyPwd);
        $encryptKey = $this->RSAEncryptByPub($AESKey, $pubKey);
        
        // step3: 使用AESKey加密报文
        $encryptData = $this->AESEncrypt($data, $AESKey);
        
        // step4: 使用私钥签名报文
        $sign = $this->sign($data, $priKey);
        
        // step5: 拼接post数据
        // 



        $post['transCode'] = 'RTPM'; //交易码  RTPM-实时代付  RPRN-实时代付结果通知
        $post['accessType'] = 0; //接入类型 0-商户接入,默认   1-平台接入
        $post['merId'] = $this->mer_no; //合作商户ID  杉德系统分配,唯一标识
        $post['plId'] = ''; //平台商户ID  平台接入必填,商户接入为空
        $post['encryptKey'] = $encryptKey;
        $post['encryptData'] = $encryptData;
        $post['sign'] = $sign;
        
        $url = $this->dfApiUrl.'/agentpay';
        $ret = $this->http_post_json($url, $post);
        parse_str($ret, $arr);
        
        try {
            // step7: 使用私钥解密AESKey
            $decryptAESKey = $this->RSADecryptByPri($arr['encryptKey'], $priKey);
            // step8: 使用解密后的AESKey解密报文
            $decryptPlainText = $this->AESDecrypt($arr['encryptData'], $decryptAESKey);
            // step9: 使用公钥验签报文
            $this->verify2($decryptPlainText, $arr['sign'], $pubKey);
            return  [
                'code'      => 200,
                'verify'    => $decryptPlainText,
                'jjson'     => json_encode($data),
                'vjson'     => json_encode($post),
                'json'      => json_encode($arr),
                'url'       => 'https://open.sandpay.com.cn/product/detail/43324/43895/',
            ];
        } catch (\Exception $e) {
            return [
                'code'          => 400,
                'err_code_des'  =>  $e->getMessage(),
                'url'           => 'https://open.sandpay.com.cn/product/detail/43324/43895/',
            ];
            echo $e->getMessage();
            exit;
        }
    }
     

    /**
     * @param $data
     * @param int $flag
     * @return void|null
     * 打印数据
     */
    public function lb_logOutput($data,$flag=0) {
        if($flag==0){
            return ;
        }
        //数据类型检测
        if (is_array($data)) {
            $data = json_encode($data);
        }
        $filename = "./".date("Y-m-d").".log";
        $str = date("Y-m-d H:i:s")."   $data"."\r\n";
        file_put_contents($filename, $str, FILE_APPEND|LOCK_EX);
        return null;
    }

    //验证
    private function verify($plainText, $sign){
        $resource = openssl_pkey_get_public($this->publicKey());
        $result   = openssl_verify($plainText, base64_decode($sign), $resource);
        openssl_free_key($resource);
        //var_dump('校验结果===========');
        //var_dump($result);
        //0是失败,  1是成功的
        return $result;
    }

    //公共key
    private function publicKey(){
        try {
            $file = file_get_contents('sandcert/sand.cer');
            if (!$file) {
                throw new \Exception('getPublicKey::file_get_contents ERROR');
            }
            $cert   = chunk_split(base64_encode($file), 64, "\n");
            $cert   = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";
            $res    = openssl_pkey_get_public($cert);
            $detail = openssl_pkey_get_details($res);
            openssl_free_key($res);
            if (!$detail) {
                throw new \Exception('getPublicKey::openssl_pkey_get_details ERROR');
            }
            return $detail['key'];
        } catch (\Exception $e) {
            throw $e;
        }
    }
    
    //获取签名字符串
    private function getSignStr($data){
        //按照键名进行升序排序
        ksort($data,SORT_NATURAL);
        $signStr = "";
        foreach ($data as $key => $value) {
            if(in_array($key, ['version','mer_no','mer_order_no','create_time','order_amt','notify_url','return_url','create_ip','pay_extra','accsplit_flag','sign_type','store_id','extend'])){
                if($value != ""){
                    if($signStr) $signStr .= "&";
                    $signStr .= "{$key}={$value}";
                }
            }
        }
        return $signStr;
    }

// ***** 代付 *******
    /**
     * 发送请求
     * @param $url
     * @param $param
     * @return bool|mixed
     * @throws Exception
     */
    private function http_post_json($url, $param)
    {
        if (empty($url) || empty($param)) {
            return false;
        }
        $param = http_build_query($param);
        ljLog($param, $url);
        try {
            $ch = curl_init();//初始化curl
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            //正式环境时解开注释
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $data = curl_exec($ch);//运行curl
            $error = curl_error($ch);
            $header = curl_getinfo($ch, CURLINFO_HEADER_OUT); 
            curl_close($ch);
    ljLog($url,'请求URL');
    ljLog($param,'请求参数');
    ljLog($data,'请求响应');
    ljLog($header,'请求响应 header');
            if (!$data) {
                throw new \Exception('请求出错');
            }
    
            return $data;
        } catch (\Exception $e) {
            throw $e;
        }
    }
    
    /**
     * 生成AESKey
     * @param $size
     * @return string
     */
    private function aes_generate($size)
    {
        $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        $arr = array();
        for ($i = 0; $i < $size; $i++) {
            $arr[] = $str[mt_rand(0, 61)];
        }
    
        return implode('', $arr);
    }
    
    /**
     * 获取公钥
     * @param $path
     * @return mixed
     * @throws Exception
     */
    private function loadX509Cert($path)
    {
        try {
            $file = file_get_contents($path);
            if (!$file) {
                throw new \Exception('loadx509Cert::file_get_contents ERROR');
            }
    
            $cert = chunk_split(base64_encode($file), 64, "\n");
            $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";
    
            $res = openssl_pkey_get_public($cert);
            $detail = openssl_pkey_get_details($res);
            openssl_free_key($res);
    
            if (!$detail) {
                throw new \Exception('loadX509Cert::openssl_pkey_get_details ERROR');
            }
    
            return $detail['key'];
        } catch (\Exception $e) {
            throw $e;
        }
    }
    /**
     * 获取私钥
     * @param $path
     * @param $pwd
     * @return mixed
     * @throws Exception
     */
    private function loadPk12Cert($path, $pwd)
    {
        try {
            $file = file_get_contents($path);
            if (!$file) {
                throw new \Exception('loadPk12Cert::file_get_contents');
            }
            if (!openssl_pkcs12_read($file, $cert, $pwd)) {
                throw new \Exception('loadPk12Cert::openssl_pkcs12_read ERROR');
            }
            return $cert['pkey'];
        } catch (\Exception $e) {
            throw $e;
        }
    }
    /**
     * 私钥签名
     * @param $plainText
     * @param $path
     * @return string
     * @throws Exception
     */
    private function sign($plainText, $path)
    {
        if(is_array($plainText)) $plainText = json_encode($plainText);
        ljLog($plainText, '签名字符串');
        try {
            $resource = openssl_pkey_get_private($path);
            $result = openssl_sign($plainText, $sign, $resource);
            openssl_free_key($resource);
    
            if (!$result) {
                throw new \Exception('签名出错' . $plainText);
            }
    
            return base64_encode($sign);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * 公钥加密AESKey
     * @param $plainText
     * @param $puk
     * @return string
     * @throws Exception
     */
    private function RSAEncryptByPub($plainText, $puk)
    {
        if (!openssl_public_encrypt($plainText, $cipherText, $puk, OPENSSL_PKCS1_PADDING)) {
            throw new \Exception('AESKey 加密错误');
        }
    
        return base64_encode($cipherText);
    }
    /**
     * 私钥解密AESKey
     * @param $cipherText
     * @param $prk
     * @return string
     * @throws Exception
     */
    private function RSADecryptByPri($cipherText, $prk)
    {
        if (!openssl_private_decrypt(base64_decode($cipherText), $plainText, $prk, OPENSSL_PKCS1_PADDING)) {
            throw new \Exception('AESKey 解密错误');
        }
    
        return (string)$plainText;
    }

    /**
     * AES加密
     * @param $plainText
     * @param $key
     * @return string
     * @throws \Exception
     */
    private function AESEncrypt($plainText, $key)
    {
        $plainText = json_encode($plainText);
        $result = openssl_encrypt($plainText, 'AES-128-ECB', $key, 1);
    
        if (!$result) {
            throw new \Exception('报文加密错误');
        }
    
        return base64_encode($result);
    }
    /**
     * AES解密
     * @param $cipherText
     * @param $key
     * @return string
     * @throws \Exception
     */
    private function AESDecrypt($cipherText, $key)
    {
        $result = openssl_decrypt(base64_decode($cipherText), 'AES-128-ECB', $key, 1);
    
        if (!$result) {
            throw new \Exception('报文解密错误', 2003);
        }
    
        return $result;
    }

    /**
     * 公钥验签
     * @param $plainText
     * @param $sign
     * @param $path
     * @return int
     * @throws Exception
     */
    private function verify2($plainText, $sign, $path)
    {ljLog($plainText, 'orderRefundApi verify2');
        $resource = openssl_pkey_get_public($path);
        $result = openssl_verify($plainText, base64_decode($sign), $resource);
        openssl_free_key($resource);
    
        if (!$result) {
            throw new \Exception('签名验证未通过,plainText:' . $plainText . '。sign:' . $sign, '02002');
        }
    
        return $result;
    }
}

4、\app\massage\controller\IndexOrder.php 下单

  * @author chenniang
     * @DataTime: 2021-03-22 09:53
     * @功能说明:下单
     */
    public function payOrder(){

        $input = $this->_input;

        $address_order_model = new OrderAddress();

        $address_model       = new Address();

        $coupon_record_model = new CouponRecord();

        $coach_model         = new Coach();

        $cap_dis[] = ['user_id','=',$this->getUserId()];
        //查看是否是团长
        $my_cap_id = $coach_model->where($cap_dis)->value('id');

        $cap_info  = $coach_model->dataInfo(['id'=>$input['coach_id']]);

        $order_id = !empty($input['order_id'])?$input['order_id']:0;

        if($input['coach_id']==$my_cap_id){

            //$this->errorMsg('技师不能给自己下单');
        }

        if($cap_info['is_work']==0){

            $this->errorMsg('该技师未上班');

        }

        if($cap_info['status']!=2){

            $this->errorMsg('该技师已下架');

        }

        $coupon_id  = !empty($input['coupon_id'])?$input['coupon_id']:0;
        //加钟订单
        if(!empty($order_id)){

            $p_order = $this->model->dataInfo(['id'=>$order_id]);

            $can_add = $this->model->orderCanAdd($p_order);

            if($can_add==0){

                $this->errorMsg('该订单不能加钟');
            }

            $address = $p_order['address_info'];

            $address['id'] = $address['address_id'];
            //加钟订单不计算车费
            $input['car_type'] = 0;
            //加钟
            $input['start_time'] = $this->model->addOrderTime($order_id)+1;
            
        }else{

            $address = $address_model->dataInfo(['id'=>$input['address_id']]);
        }

        if(empty($address)){

            $this->errorMsg('请添加地址');
        }

        $order_info = $this->model->payOrderInfo($this->getUserId(),$input['coach_id'],$address['lat'],$address['lng'],$input['car_type'],$coupon_id,$order_id);

        $config_model = new Config();

        $config = $config_model->dataInfo(['uniacid'=>$this->_uniacid]);

        Db::startTrans();

        $key = $order_info['coach_id'].$input['start_time'].'order_key';
        
        incCache($key,1,$this->_uniacid);

        $key_value = getCache($key,$this->_uniacid);

        if($key_value!=1){

            decCache($key,1,$this->_uniacid);

            $this->errorMsg('下单人数过多,请重试');

        }
        //检查技师时间(返回结束时间)
        $check = $this->model->checkTime($order_info,$input['start_time'],$order_id);

        if(!empty($check['code'])){

            decCache($key,1,$this->_uniacid);

            $this->errorMsg($check['msg']);
        }

        //默认微信
        $pay_model = isset($input['pay_model'])?$input['pay_model']:1;

        $order_insert = [

            'uniacid'    => $this->_uniacid,

            'over_time'  => time()+$config['over_time']*60,

            'order_code' => orderCode(),

            'user_id'    => $this->getUserId(),

            'pay_price'  => $order_info['pay_price'],

            'balance'    => $pay_model==2?$order_info['pay_price']:0,

            'init_service_price'=> $order_info['init_goods_price'],

            'service_price'=> $order_info['goods_price'],

            'true_service_price' => $order_info['goods_price'],

            'discount'    => $order_info['discount'],

            'car_price'   => $order_info['car_price'],

            'true_car_price' => $order_info['car_price'],

            'pay_type'    => 1,

            'coach_id'    => $order_info['coach_id'],

            'start_time'  => $input['start_time'],

            'end_time'    => $check['end_time'],

            'distance'    => $order_info['distance'],

            'time_long'   => $check['time_long'],

            'true_time_long' => $check['time_long'],
            //备注
            'text'        => !empty($input['text'])?$input['text']:'',

            'can_tx_time' => $config['can_tx_time'],

            'car_type'    => $input['car_type'],

            'channel_id'  => !empty($input['channel_id'])?$input['channel_id']:0,

            'app_pay'     => $this->is_app,
            //技师出发地址
            'trip_start_address' => $cap_info['address'],
            //订单到达地址
            'trip_end_address' => $address['address'].' '.$address['address_info'],
            //加钟fu
            'add_pid' => $order_id,

            'is_add'  => !empty($order_id)?1:0,

            'pay_model' => $pay_model

        ];
        //下单
        $res = $this->model->dataAdd($order_insert);

        if($res!=1){

            decCache($key,1,$this->_uniacid);

            Db::rollback();

            $this->errorMsg('下单失败');
        }

        decCache($key,1,$this->_uniacid);

        $order_id = $this->model->getLastInsID();
        //使用优惠券
        if(!empty($coupon_id)){

            $coupon_id = $coupon_record_model->couponUse($coupon_id,$order_id);

            $this->model->dataUpdate(['id'=>$order_id],['coupon_id'=>$coupon_id]);

        }
        //添加下单地址
        $res = $address_order_model->orderAddressAdd($address['id'],$order_id);

        if(!empty($res['code'])){

            Db::rollback();

            $this->errorMsg($res['msg']);
        }

        if(empty($order_info['order_goods'])){

            Db::rollback();

            $this->errorMsg('请选择服务项目,请刷新重试');
        }
        //添加到子订单
        $res = $this->order_goods_model->orderGoodsAdd($order_info['order_goods'],$order_id,$input['coach_id'],$this->getUserId());

        if(!empty($res['code'])){

            Db::rollback();

            $this->errorMsg($res['msg']);
        }

        $order_insert_data = $this->model->dataInfo(['id'=>$order_id]);
        //处理各类佣金情况
        $order_update = $this->model->getCashData($order_insert_data);

        if(!empty($order_update['code'])&&$order_update['code']==300){

            $this->errorMsg('请添加技师等级');

        }

        $res = $this->model->dataUpdate(['id'=>$order_id],$order_update['order_data']);
        
        Db::commit();
        //如果是0元
        if($order_insert['pay_price']<=0){

            $this->model->orderResult($order_insert['order_code'],$order_insert['order_code']);

            return $this->success(true);
        }
        //余额支付
        if($pay_model==2){

            $user_model = new User();

            $user_balance= $user_model->where(['id'=>$this->getUserId()])->value('balance');

            if($user_balance<$order_insert['pay_price']){

                $this->errorMsg('余额不足');
            }

            $this->model->orderResult($order_insert['order_code'],$order_insert['order_code']);

            return $this->success(true);

        }elseif ($pay_model==3){

            $pay_model = new PayModel($this->payConfig());

            $jsApiParameters = $pay_model->aliPay($order_insert['order_code'],$order_insert['pay_price'],'按摩订单');

            $arr['pay_list']= $jsApiParameters;

            $arr['order_code']= $order_insert['order_code'];
//增加的杉德宝微信支付
        }elseif ($pay_model==10){
            //微信支付(衫德)
            $pay_controller = new \app\shop\controller\IndexSandPay($this->app);
            //支付
            $jsApiParameters= $pay_controller->createWeixinPay($this->payConfig(),$this->getUserInfo()['openid'],$this->_uniacid,"anmo",['type' => 'Massage' , 'out_trade_no' => $order_insert['order_code']],$order_insert['pay_price'], $return_url = $input['return_url'] ?? '');

            $arr['pay_list']= $jsApiParameters;
 //增加的杉德宝微信支付
        }else{
            //微信支付
            $pay_controller = new \app\shop\controller\IndexWxPay($this->app);
            //支付
            $jsApiParameters= $pay_controller->createWeixinPay($this->payConfig(),$this->getUserInfo()['openid'],$this->_uniacid,"anmo",['type' => 'Massage' , 'out_trade_no' => $order_insert['order_code']],$order_insert['pay_price']);

            $arr['pay_list']= $jsApiParameters;
        }

        return $this->success($arr);

    }

5、重新支付

/**
     * @author chenniang
     * @DataTime: 2021-03-25 15:59
     * @功能说明:重新支付
     */
    public function rePayOrder(){

        $input = $this->_input;

        $order_insert = $this->model->dataInfo(['id'=>$input['id']]);

        if($order_insert['pay_type']!=1){

            $this->errorMsg('订单状态错误');

        }

        if($order_insert['app_pay']==1&&$this->is_app!=1){

            $this->errorMsg('请到APP完成支付');

        }

        if($order_insert['app_pay']==0&&$this->is_app!=0){

            $this->errorMsg('请到小程序完成支付');
        }

        if($order_insert['app_pay']==2&&$this->is_app!=2) {

            $this->errorMsg('请到公众号完成支付');

        }

        if($order_insert['pay_model']==2){

            $user_model = new User();

            $user_balance= $user_model->where(['id'=>$this->getUserId()])->value('balance');

            if($user_balance<$order_insert['pay_price']){

                $this->errorMsg('余额不足');
            }

            $this->model->orderResult($order_insert['order_code'],$order_insert['order_code']);

            return $this->success(true);

        }elseif ($order_insert['pay_model']==3){

            $pay_model = new PayModel($this->payConfig());

            $jsApiParameters = $pay_model->aliPay($order_insert['order_code'],$order_insert['pay_price'],'按摩订单');

            $arr['pay_list']= $jsApiParameters;

            $arr['order_code']= $order_insert['order_code'];

//增加的杉德宝微信支付
        }elseif ($order_insert['pay_model']==10){
            $new_order_code = orderCode();
            $this->model->dataUpdate(['id'=>$order_insert['id']],['order_code'=>$new_order_code]);
            //杉德宝微信支付
            $pay_controller = new \app\shop\controller\IndexSandPay($this->app);
            //支付
            $jsApiParameters= $pay_controller->createWeixinPay($this->payConfig(),$this->getUserInfo()['openid'],$this->_uniacid,"anmo",['type' => 'Massage' , 'out_trade_no' => $new_order_code],$order_insert['pay_price'], $return_url = $input['return_url'] ?? '');

            $arr['pay_list']= $jsApiParameters;
//增加的杉德宝微信支付

        }else{
            //微信支付
            $pay_controller = new \app\shop\controller\IndexWxPay($this->app);
            //支付
            $jsApiParameters= $pay_controller->createWeixinPay($this->payConfig(),$this->getUserInfo()['openid'],$this->_uniacid,"anmo",['type' => 'Massage' , 'out_trade_no' => $order_insert['order_code']],$order_insert['pay_price']);

            $arr['pay_list'] = $jsApiParameters;
        }

        return $this->success($arr);

    }

6、\app\massage\model\Order.php

   /**
     * @author chenniang
     * @DataTime: 2021-03-15 14:37
     * @功能说明:后台列表
     */
    public function adminDataList($dis,$page=10){

        $data = $this->alias('a')
                ->join('massage_service_coach_list b','a.coach_id = b.id')
                ->join('massage_service_order_goods_list c','a.id = c.order_id')
                ->join('massage_service_order_address d','a.id = d.order_id')
                ->join('massage_channel_list e','a.channel_id = e.id','left')
                ->join('massage_channel_cate f','f.id = e.cate_id','left')
                ->where($dis)
                ->field('a.*,b.coach_name,d.mobile,d.user_name,e.user_name as channel_name,f.title as channel')
                ->group('a.id')
                ->order('a.id desc')
                ->paginate($page)
                ->toArray();

        if(!empty($data['data'])){

            $user_model = new User();

            $refund_model = new RefundOrder();

            foreach ($data['data'] as &$v){

                $v['nickName'] = $user_model->where(['id'=>$v['user_id']])->value('nickName');

                $v['distance'] = distance_text($v['distance']);

                $v['refund_price'] = $refund_model->where(['order_id'=>$v['id'],'status'=>2])->sum('refund_price');
                //加钟订单
                if($v['is_add']==0){

                    $v['add_order_id'] = $this->where(['add_pid'=>$v['id']])->where('pay_type','>',1)->field('id,order_code')->select()->toArray();

                }else{

                    $v['add_pid'] = $this->where(['id'=>$v['add_pid']])->field('id,order_code')->find();

                }
               //新增的
                if($v['pay_model'] == 10) $v['pay_model'] = 1;
                 //新增的
            }
        }

        return $data;

    }



版权声明:本文为withkai44原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。