對接杉德支付主要還是看人家給出的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()];
}
}
}
```
- 后端
- PHP
- php接收base64格式的圖片
- php 下載文件
- 位,字節,字符的區別
- 求模技巧
- php curl
- php 瀏覽器禁用cookie后需要使用session 就可以用url傳遞session_id
- 有用小方法
- phpDoc
- php 文件鎖來解決高并發
- php小知識
- PHP根據身份證號碼,獲取性別、獲取生日、計算年齡等多個信息
- php 獲取今天,明天、本周、本周末、本月的起始時間戳和結束時間戳的方法
- php 無限級分類
- xdebug設置
- curl
- 獲取現在距離當天結束的還有多少秒
- win10安裝php8版本報錯(Fix PHP Warning: vcruntime140.dll 14.0 is not compatible with this PHP build.)
- 有趣代碼注釋
- php array_diff用法
- parse_str 處理http的query參數
- PHP文件上傳限制
- php操作html
- php trim 函數的使用
- thinkphp5
- 定時任務不能連接數據庫
- 寶塔設置計劃任務
- 控制方法 return $data ,不能直接返回json
- tp5.1命令行
- tp3.2.3 報internal server error
- 悟空crm
- web-msg-sender的使用
- 杉德支付
- laravel
- laravel 遷移文件的使用
- laravel的安裝
- laravel 單元測試
- laravel seeder的使用
- 模型相關
- restful理解
- laravel 的表單驗證
- laravel 隊列的使用
- laravel響應宏應用macro
- laravel 判斷集合是否為空
- laravel 使用ymondesigns/jwt-auth jwt
- laravel 模型工廠
- laravel 自定義助手函數
- laravel 自帶auth的登錄
- 寶塔開啟laravel隊列
- laravel 蘋果內購
- laravel 中的.env.example
- laravel 監聽執行過的sql語句
- laravel-websockets 替代pusher 發送頻道消息
- 記laravel config配置文件目錄中不能使用 url()助手函數
- laravel使用 inspector 進行實時監控
- laravel 項目部署的配置
- laravel 刪除mongodb集合
- laravel 自定義項目命名空間
- laravel 易錯提醒
- laravel 自己組裝分頁
- laravel 設置定時任務
- laravel事件和隊列指定隊列名
- laravel 使用validate檢測名字是否唯一
- laravel + nginx 偽靜態分析
- fastadmin
- cms
- 標簽
- 模板
- dact-admin
- dcat-admin的安裝
- dcat-admin的curd使用
- dact-admin表單使用
- dcat-admin行為表單使用
- dcat-admin使用技巧
- dcat-admin自定義文件上傳
- dcat-admin的js彈窗
- dcat-admin 工具表單傳參
- dcat-admin listbox編輯回顯用法
- weixin
- 微信支付
- 支付類
- 小程序
- 微信提現類
- jwt
- lcobucci/jwt
- Firebase\JWT
- phpstudy
- nginx配置tp5 505 404 錯誤
- tp5重寫 apache
- 織夢模板 使用weight 排序
- phpstudy 添加php8.1版本
- phpstudy ERR_CONNECTION_REFUSED
- phpstudy 設置多個版本php
- 阿里云
- 支付寶支付
- 阿里云短信
- 阿里云OSS上傳圖片報錯
- 阿里云號碼認證(一鍵登錄)
- send login code error: 發送驗證碼失敗:cURL error 28: Connection timed out after 5001 milliseconds
- 極光號碼認證(一鍵登錄)
- git使用
- git
- sentry專欄
- sentry的私有化部署
- sentry設置郵箱
- sentry設置url地址
- sentry中KafkaError OFFSET_OUT_OF_RANGE error
- centos
- tar 壓縮解壓
- centos 8 Errors during downloading metadata for repository 'appstream'
- vim的使用
- ssh秘鑰登錄
- 修改了.bashrc不能立即生效
- 設置軟連接
- 使用echo清空文件內容
- 查看文件大小
- centos8 設置靜態ip
- nginx
- nginx的學習
- nginx配置wss
- supervisor的使用
- shell的使用
- 數據庫
- mysql
- mysql的事務隔離級別
- mysql共享鎖和排它鎖
- mysql的三范式
- mysql 在那些場景下索引會失效
- mysql 的書寫順序
- mysql case用法
- mysql 以逗號分割字符串
- msyql innodb 行鎖解決高并發
- mysql修改字符集
- 鎖
- 樂觀鎖悲觀鎖
- mysql 最左索引原則
- mysql 同表兩列值互換
- mysql升序排列字段為0的在最后
- mysql case when then else end 語法
- mysql 常見錯誤
- mysql json用法
- MongoDB
- mongodb安裝
- redis
- redis 常用通用命令
- string類型的常見命令
- 連接遠程redis刪除指定的值
- markdown
- markdown的使用
- github
- github使用小技巧
- jenkins
- 安裝jenkins
- jenkins設置時區
- docker
- 安裝docker
- docker容器設為自啟動和取消容器自啟動
- docker 安裝mysql
- docker-compose
- docker 安裝php
- docker-compose安裝nginx
- docker-compose安裝php
- docker安裝php+supervisor
- composer使用
- composer
- win10檢查端口占用
- 局域網內同事訪問自己的項目
- 本地測試設置https辦法
- 正則表達式
- 前端代碼和后臺代碼部署在一起的解決方法
- pc微信抓包小程序
- xshell一年后提示需要更新才能打開
- 使用ssh秘鑰登錄服務器
- supervisor
- supervisor的使用
- 瀏覽器的強制緩存和協商緩存
- window11下ssh遠程登錄服務器
- chatgpt
- 注冊chatgpt
- 第三方chatgpt地址
- 前端
- jquery 常用方法
- jquery 省市區三級聯動
- 百度地圖短地址
- npm
- webpack
- vue
- 谷歌安裝vue-devtools的使用
- swiper 一屏顯示頁面
- 騰訊地圖
- jquery點擊圖片放大
- 移動端rem適配
- 彈性布局flex
- CSS
- box-sizing
- 移動端去掉滾動條
- 三角形
- 樹形結構
- require.js的使用
- 微擎人人商城
- 人人商城彈出框
- 常用方法
- 客服消息
- 企業支付到零錢
- 修復權限問題
- 獲取access_token
- 其他管理員沒有應用 調用不了p方法
- 修改公眾號推送消息
- 人人商城
- 人人商城二開常見問題
- 人人商城應用顯示隱藏
- 微擎
- 人人商城小程序解密登錄
- 面試題
- 遍歷目錄中的文件和目錄
- 冒泡排序
- php 在字符串中找到最長對稱字符串
- 地圖相關
- 百度地圖根據ip獲取地址
- 百度,騰訊,高德,地圖點擊跳轉
- 百度地圖根據地址獲取經緯度
- 百度地圖和騰訊地圖經緯度互轉
- 其他
- B站跳過充電環節
- 可愛貓咪回收站制作(附圖)
- 程序員變量命名網站
- 解決谷歌瀏覽器強制跳轉https
- 隨機密碼生成
- 編輯器
- vs code使用
- phpstrom
- phpstrom 常用命令
- phpstrom ctrl+b后想回到之前的位置
- phpstrom 批量操作下劃線轉駝峰
- phpstrom 插件
- phpstrom 使用ctrl+shift+f后搜索不能輸入中文
- phpstrom中項目.env文件會自動消失,不顯示
- vscode插件