首页 > 其他分享 >杉德支付

杉德支付

时间:2022-08-26 14:33:53浏览次数:57  
标签:Exception return plainText self param 杉德 支付 result

对接杉德支付主要还是看人家给出的demo和文档 以下文件都是根据官方给出的demo修改而来的

配置类



<?php
namespace sandpay;
use Exception;
use think\Db;

class SandConfig
{
    //--------------------------------------------1、基础参数配置------------------------------------------------
    const API_HOST = 'https://cashier.sandpay.com.cn/gateway/api';
    const PUB_KEY_PATH = __DIR__ .'/cert/sand.cer'; //公钥文件  官网可以直接下载
    const PRI_KEY_PATH = __DIR__ .'/cert/private_key.pfx'; //私钥文件 给出的邮件里面可以使用360浏览器,官方推荐IE浏览器,具体流程可以问客服怎么导出证书,需要导出公钥和私钥证书文件  私钥的在这里,公钥的上传到杉德支付的商服后台。
    const CERT_PWD = 123456; //私钥证书密码 这个秘钥是导出私钥的时候设置的 自定义 一定要记住密码

    const MID = '000000000'; // 商户ID
    const FRONT_URL = ''; //  前端地址

    // 微信appid
    public $wxappid=null;
    // 异步地址
    public $notify_url=null;

    public function __construct()
    {
        if(is_null($this->wxappid))  $this->wxappid=Db::name('wxconfig')->where(['status'=>1])->value('appid');
        if(is_null($this->notify_url)) $this->notify_url=request()->domain().'/api/order/sandNotify';
    }

    //--------------------------------------------end基础参数配置------------------------------------------------
    /**
     * 获取公钥
     * @param $path
     * @return mixed
     * @throws Exception
     */
    public 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
     */
    public 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
     */
    public function sign($plainText, $path)
    {
        $plainText = json_encode($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;
        }
    }

    /**
     * 公钥验签
     * @param $plainText
     * @param $sign
     * @param $path
     * @return int
     * @throws Exception
     */
    public function verify($plainText, $sign, $path)
    {
        $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;
    }

    /**
     * 发送请求
     * @param $url
     * @param $param
     * @return bool|mixed
     * @throws Exception
     */
    public function http_post_json($url, $param)
    {
        if (empty($url) || empty($param)) {
            return false;
        }
        $param = http_build_query($param);
        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);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);  #成功连接服务器前等待时长
            curl_setopt($ch, CURLOPT_TIMEOUT, 30); # 从服务器接收缓冲完成前需要等待多长时间
            $data = curl_exec($ch);//运行curl
            curl_close($ch);

            if (!$data) {
                throw new \Exception('请求出错');
            }
            return $data;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    public function parse_result($result)
    {
        $arr = array();
        $response = urldecode($result);
        $arrStr = explode('&', $response);
        foreach ($arrStr as $str) {
            $p = strpos($str, "=");
            $key = substr($str, 0, $p);
            $value = substr($str, $p + 1);
            $arr[$key] = $value;
        }
        return $arr;
    }


    /**
     * 公钥加密AESKey
     * @param $plainText
     * @param $puk
     * @return string
     * @throws
     */
    public 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
     */
    public 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
     */
    public 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
     */
    public function AESDecrypt($cipherText, $key)
    {
        $result = openssl_decrypt(base64_decode($cipherText), 'AES-128-ECB', $key, 1);

        if (!$result) {
            throw new \Exception('报文解密错误', 2003);
        }

        return $result;
    }

    /**
     * 生成AESKey
     * @param int $size
     * @return string
     */
    public function aes_generate(int $size)
    {
        $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        $arr = array();
        for ($i = 0; $i < $size; $i++) {
            $arr[] = $str[mt_rand(0, 61)];
        }
        return implode('', $arr);
    }

}

支付文件(直接把这个参数给前端就行 就是支付需要的参数)


<?php
namespace sandpay;
use think\Request;
use \sandpay\SandConfig;
/**
 * Class Pay
 * @package sandpay
 */
class Pay{

    protected static $error_info='服务器繁忙,请稍后再试!';

    protected static $config=null;

    public function __construct()
    {
        if(self::$config == null) self::$config=new SandConfig();
    }


    /**
     * 杉德支付
     * @param $orderCode 商户订单号
     * @param $money  订单金额
     * @param $userId  type=1 微信 传递openid  type=0 支付宝 userid
     * @param $type  1 微信 0 支付宝
     * @return array
     * @throws \Exception
     */
    public function start($orderCode,$money,$userId,$type=1){
//            $money=0.01;
            // step1: 拼接data
            $rand=mt_rand(10,99);
            $data = array(
                'head' => array(
                    'version' => '1.0',  // 版本号 默认1.0
                    'method' => 'sandpay.trade.pay', // "请求地址-method"  sandpay.trade.pay
                    'productId' =>$type ? '00002020' : '00002022', // 产品编码 https://open.sandpay.com.cn/product/detail/43984//   // 微信公众号 00002020
                    'accessType' => '1', // 接入类型   1-普通商户接入  2-平台商户接入 3-核心企业商户接入
                    'mid' =>SandConfig::MID, // 商户ID 收款方商户号
                    'channelType' => '07',  // 渠道类型 07-互联网    08-移动端
                    'reqTime' => date('YmdHis', time())  // 请求时间 格式:yyyyMMddhhmmss
                ),
                'body' => array(
                    'orderCode' =>$orderCode.$rand,  // 商户订单号 长度12位起步,商户唯一,建议订单号有日期
                    'totalAmount' =>str_pad($money * 100,12,"0",STR_PAD_LEFT), // 订单金额 例000000000101代表1.01元
                    'subject' =>'购买商品',//订单标题
                    'body' =>'杉德支付',// 订单描述
                    'payMode' => $type ? 'sand_wx' : 'sand_alipay',// 支付模式  sand_wx 微信公众号/小程序支付
                    "payExtra"=>json_encode($type ? [
                        'subAppid' => self::$config->wxappid,
                        'userId' => $userId
                    ] : [
                        'userId' => $userId
                    ]),// 支付扩展域 类型为josn
                    'clientIp' =>request()->ip(),//客户端IP 127.0.0.1
                    'notifyUrl' =>self::$config->notify_url, // 异步通知地址  1.https 2.直接可以访问
//                    'frontUrl' =>request()->domain().'/index.html#/',//前台通知地址
                    'extend'=>$type,
                )
            );
            // step2: 私钥签名
            $prikey =self::$config->loadPk12Cert(SandConfig::PRI_KEY_PATH, SandConfig::CERT_PWD);
            $sign = self::$config->sign($data, $prikey);

            // step3: 拼接post数据
            $post = array(
                'charset' => 'utf-8',
                'signType' => '01',
                'data' => json_encode($data),
                'sign' => $sign
            );

            // step4: post请求
            $result =self::$config->http_post_json(SandConfig::API_HOST . '/order/pay', $post);
            $arr = self::$config->parse_result($result);
            //step5: 公钥验签
            $pubkey =self::$config->loadX509Cert(SandConfig::PUB_KEY_PATH);
            try {
                self::$config->verify($arr['data'], $arr['sign'], $pubkey);
            } catch (\Exception $e) {
                return $e->getMessage();
            }
            // step6: 获取credential
            $data = json_decode($arr['data'], true);
            if ($data['head']['respCode'] == "000000") {
                  $datas=json_decode($data['body']['credential'],true);
                  if(!isset($datas['params'])){
                        throw new \Exception("字符串解析错误");
                  }
//                $data = json_decode($data['params'],true);
//                $result = substr($data['payInfo']['trade_no'],-28,28);
                return json_decode($datas['params'],true);
            } else {
                return self::$error_info;
            }
        }
}

代付文件



<?php
namespace sandpay;
use Exception;
use think\Db;
use \sandpay\SandConfig;

class Withdrawal
{

    protected static $url='https://caspay.sandpay.com.cn/agent-main/openapi/agentpay';

    protected static $error_info='服务器繁忙,请稍后再试!';

    protected static $config=null;

    public function __construct()
    {
        if(self::$config == null) self::$config=new SandConfig();
    }

    /**
     * 付款到银行卡
     * @param $params
     * @return bool
     */
    public function start($params)
    {
        $info = array(
            'transCode' => 'RTPM', // 实时代付
            'merId' => SandConfig::MID, // 此处更换商户号
            'url' => '/agentpay',
            'pt' => array(
                'orderCode' => $params['order_sn'],
                'version' => '01',
                'productId' => '00000004',
                'tranTime' => date('YmdHis', time()),
//                'timeOut' => '20181024120000',//不填默认24小时
                'tranAmt' => str_pad($params['amount'] * 100,12,"0",STR_PAD_LEFT),
                'currencyCode' => '156',
                'accAttr' => '0',
                'accNo' => $params['bank_no'],
                'accType' => '4',
                'accName' => $params['bank_real_name'],
//        'provNo' => 'sh',
//        'cityNo' => 'sh',
//                'bankName' => 'cbc',//对公代付必填
//            'bankType' => '1',//对公代付必填
                'remark' => $params['bank_real_name'],
                'payMode' => 'desc',
                'channelType' => '07'
            )
        );
        // step1: 拼接报文及配置
        $transCode = $info['transCode']; // 交易码
        $accessType = '0'; // 接入类型 0-商户接入,默认;1-平台接入
        $merId = $info['merId']; // 此处更换商户号
        $path = $info['url']; // 服务地址
        $pt = $info['pt']; // 报文

        try {
            // step2: 生成AESKey并使用公钥加密
            $AESKey = self::$config->aes_generate(16);
            $pubKey = self::$config->loadX509Cert(SandConfig::PUB_KEY_PATH);
            $priKey = self::$config->loadPk12Cert(SandConfig::PRI_KEY_PATH,SandConfig::CERT_PWD);
            $encryptKey = self::$config->RSAEncryptByPub($AESKey, $pubKey);

            // step3: 使用AESKey加密报文
            $encryptData = self::$config->AESEncrypt($pt, $AESKey);

            // step4: 使用私钥签名报文
            $sign = self::$config->sign($pt,$priKey);

            // step5: 拼接post数据
            $post = array(
                'transCode' => $transCode,
                'accessType' => $accessType,
                'merId' => $merId,
                'encryptKey' => $encryptKey,
                'encryptData' => $encryptData,
                'sign' => $sign
            );
            // step6: post请求
            $result = self::$config->http_post_json(self::$url, $post);
            parse_str($result, $arr);
            // step7: 使用私钥解密AESKey
            $decryptAESKey = self::$config->RSADecryptByPri($arr['encryptKey'], $priKey);

            // step8: 使用解密后的AESKey解密报文
            $decryptPlainText = self::$config->AESDecrypt($arr['encryptData'], $decryptAESKey);
            // step9: 使用公钥验签报文
            self::$config->verify($decryptPlainText, $arr['sign'],$pubKey);

            $decryptPlainText = json_decode($decryptPlainText,true);
            if((int)$decryptPlainText['respCode'] > 2 && $decryptPlainText['resultFlag'] == 1){
                throw new \Exception($decryptPlainText['respDesc']);
            }
            return ['code'=>1,'info'=>'ok'];
        } catch (\Exception $e) {
            return ['code'=>0,'info'=>$e->getMessage()];
        }
    }

}

标签:Exception,return,plainText,self,param,杉德,支付,result
From: https://www.cnblogs.com/xiaobo0925/p/16627476.html

相关文章

  • vue3支付后不允许跳转回预付订单页
    场景:在电商项目中,订单页通常是用来展示给用户看的,当支付完成后,就不允许回退回来,防止二次支付以及商品信息错乱曾使用:router.push(`/member/pay?orderId=${res.data.resul......
  • Android最新微信支付总结
    签名和包名,签名的话:用release。关于上线:非硬性指标。1.申请appid提交应用,等待审核。只有审核通过后,才能有开发资格。支付认证费:300元/年。每年需要认证一次。2.下载sdk......
  • 【面试】【5】如何取消超时未支付的订单?
    1、数据库轮询使用一个线程定时的去扫描数据库,通过订单时间来判断是否有超时的订单,然后取消操作缺点:存在延时,最差的延迟时间是设置的扫描间隔时间;对数据库性能消......
  • DelphiXE微信支付&支付宝支付源代码
    下载http://www.htsoft.com.cn/download/Delphi7_AlliSms_Demo.rar在项目中,如果要实现短信发送功能,需要调用第三方提供的短信服务,市面上有很多第三方提供的短信服务,这些......
  • Uniapp---IOS无法支付的问题
    开发好的APP,最近内部测试,在安卓端调起支付没有问题,但是在IOS端,却无法进行支付,报错:支付失败,原因requestPayment:failservicenotfound论坛上的相同问题:https://ask......
  • “支付功能”怎么测试么?
    作为测试,不管是面试还是笔试,必然要被考验到的就是”测试思维“。在面试中就是体现在如下面试题中:“说说你项目中的xx模块你是如何测试的?”“给你一个购物车,你要怎么测......
  • mybatis事务-支付流程笔记-含建表代码
    相关表:SETNAMESutf8mb4;SETFOREIGN_KEY_CHECKS=0;--------------------------------Tablestructureforgoods_info------------------------------DROP......
  • 支付宝网页端 调用沙箱 支付环境
    <dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.33.1.ALL</version></dependency>@Datapublicclass......
  • 抖音小程序支付开发
    1、签名工具类usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Security.Cryptography;usingSystem.Te......