|
|
|
|
@ -0,0 +1,223 @@
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
|
|
|
|
|
use App\Models\WeChatPay;
|
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
|
use Yo;
|
|
|
|
|
use function Symfony\Component\String\s;
|
|
|
|
|
|
|
|
|
|
class TongLianPayController
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
public static $cusid = "56447508999A87S";//商户号 正式
|
|
|
|
|
public static $appid = "00352016";//appid 正式
|
|
|
|
|
public static $key="MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCS6r706Ogi7FpYV71ccH1jwC2NZGcrWzt1Xw6rJ6+ZEeICQjUWZLPqc/D4fvh3xJPaxIqXlcA5y9xlPVO7hhoVqAKY3uPC519j6IFBxIUYC0fKWZdCpBK/kfcaqZu6ChH+hmawqfwqRjYeIGJLW2xyS8GAxQX6Uj32bZHW0xfl6Ia6lmVZ2pNpO8RS8ExhhuNc4Mm9G72+drQKfj809NNwO7fzNWa6htogszbhcf+n5pBqSzGn6j0RAnq6Eylza8GOt6LXI04KpvglPsvdCgJYK2VdezADG3XjBSdFo7ezyh0YRYVgzEWpAA1bFe6eOJPsMccetxVr/s1PwDXg3kMnAgMBAAECggEARUHpRA3ZhMETs+qkp0OWMKRjNPC8W+l1pZZqlU+vjUKrXvI0EXrmf6MpWRON8PPX2XzmcqATDovmOUmIQ/HrMQNTOhp0lyg4RSJRcJ4COfvzW1YRosYy2mEmcki7Ux+1LN3R4f04FPulPl14yNpvzI+eyCPY9hC4CgfMBM/dR7E3nU/kpesGhfwPs/prXHs9Dy/r6YG7RjSF/QANga8rVFMnQJpxSYBABRVRePtTThTZ8fTgabLpqXOihqhoBSYpt0sP5FK7cMOKJn93H4Y2UbSzcLZRdbm1vjuVILxyHe1XQ72gFbHucy1XbjQ+GKMLaHkXx9ikqjaeg5lQIWpWCQKBgQDCCLGu9p4cy8paRTykf7El0SaPYcsW8JrJqfd5USV/gKNGJ9N+mGxEo+RszD61L5Vufniupzbfepl1kCVJGQ2lqgfJe1r3RToF8PPIvq/IdBfcVpIOeIf6y/JK0OJwBnk4WwTprWIicUDW5Ju+vvddxOL2xAr+f+pZJG79PdYDfwKBgQDB1f0CE3ExhzfznQyLO7T418qXrmzSkp0qi7KS+zNWvamgHNBovqsZsS604lH6CTag8FPOuvPUaNiDFWV6lbQ7ve8Ta9xtQ6if3yNlARliHgWhM/Cy+BLTC4+1Eq2cD62ud7tFbxxcio60PVihGLsyiVSJn4X/W3QGksGSot70WQKBgQCDl9/huHJNN00aYcCaE9vxuOI89ecFvNJNmM804PrceULCjroaLoenGbwOqYqR2R6wXGe+k7tcX0p6d/NBmxMtWp6DX/y2nKWYZ+z+JY0UFuAj2DagR9B5IB5H7VjU/x7xQJGZZbSbhVGjLFIQUHV1i9Ud5KFMLzahlFsNtwVRQwKBgQCLuDvPq0QEd3uVVrccBcXFOYcHHuvCPXyclmX6HKg89im3CA4R8p5Svqz0Jc8BdHK0P4eplIj9qzWmB15GpnJsPdR/I1iYShTaaHWS8N/jIdDdChuOZdLr3YGQ/A24WHTRNez/d7/x1M5YjX+9X55QJz2QljR4hOt1cYOmt1AOwQKBgQCgb4HhAo/IRDifuXQc5SOgbM/woZ4/9Yxbdv3f9vBX9xRfcJ9deFsi513jgow6LF0+eS6rh189Ds/kXM1/wgC4dg+6rrW3x/gYXSXv6gaWTmYF0xtkS1TV/7JIu1CJmxA91aJEODEdjBQZWoFqZiQlTE1t2CuPsvTDq/HpedEJ5g==";
|
|
|
|
|
|
|
|
|
|
//文档测试key
|
|
|
|
|
//public static $key='MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJgHMGYsspghvP+yCbjLG43CkZuQ3YJyDcmEKxvmgblITfmiTPx2b9Y2iwDT9gnLGExTDm1BL2A8VzMobjaHfiCmTbDctu680MLmpDDkVXmJOqdlXh0tcLjhN4+iDA2KkRqiHxsDpiaKT6MMBuecXQbJtPlVc1XjVhoUlzUgPCrvAgMBAAECgYAV9saYTGbfsdLOF5kYo0dve1JxaO7dFMCcgkV+z2ujKtNmeHtU54DlhZXJiytQY5Dhc10cjb6xfFDrftuFcfKCaLiy6h5ETR8jyv5He6KH/+X6qkcGTkJBYG1XvyyFO3PxoszQAs0mrLCqq0UItlCDn0G72MR9/NuvdYabGHSzEQJBAMXB1/DUvBTHHH4LiKDiaREruBb3QtP72JQS1ATVXA2v6xJzGPMWMBGQDvRfPvuCPVmbHENX+lRxMLp39OvIn6kCQQDEzYpPcuHW/7h3TYHYc+T0O6z1VKQT2Mxv92Lj35g1XqV4Oi9xrTj2DtMeV1lMx6n/3icobkCQtuvTI+AcqfTXAkB6bCz9NwUUK8sUsJktV9xJN/JnrTxetOr3h8xfDaJGCuCQdFY+rj6lsLPBTnFUC+Vk4mQVwJIE0mmjFf22NWW5AkAmsVaRGkAmui41Xoq52MdZ8WWm8lY0BLrlBJlvveU6EPqtcZskWW9KiU2euIO5IcRdpvrB6zNMgHpLD9GfMRcPAkBUWOV/dH13v8V2Y/Fzuag/y5k3/oXi/WQnIxdYbltad2xjmofJ7DbB7MJqiZZD8jlr8PCZPwRNzc5ntDStc959';
|
|
|
|
|
|
|
|
|
|
public static $PayUrl = "https://vsp.allinpay.com/apiweb/unitorder/pay";//统一支付 正式
|
|
|
|
|
public static $PayCheckUrl = "https://vsp.allinpay.com/apiweb/tranx/query";//支付查询
|
|
|
|
|
public static $RefundUrl = "https://vsp.allinpay.com/apiweb/tranx/refund";//退款查询
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function PrivatedKey($key)
|
|
|
|
|
{
|
|
|
|
|
return "-----BEGIN PRIVATE KEY-----\n" .
|
|
|
|
|
chunk_split($key, 64) .
|
|
|
|
|
"-----END PRIVATE KEY-----";
|
|
|
|
|
}
|
|
|
|
|
public function StartPay($orderInfo)
|
|
|
|
|
{
|
|
|
|
|
// 要签名的数据(包含 sign 字段,但会被排除)
|
|
|
|
|
$data = [
|
|
|
|
|
'appid' => self::$appid,
|
|
|
|
|
'cusid' => self::$cusid,
|
|
|
|
|
'version'=>11,
|
|
|
|
|
'trxamt'=>$orderInfo['true_price'],//交易金额
|
|
|
|
|
'reqsn'=> $orderInfo['show'],//订单号
|
|
|
|
|
'paytype'=>'W06',//交易方式 W06小程序
|
|
|
|
|
'randomstr'=>bin2hex(random_bytes(32 / 2)),
|
|
|
|
|
'body'=>'微信小程序体检订单',//订单标题
|
|
|
|
|
'notify_url'=>env('APP_URL') . '/api/Pay/TongLian/callback',
|
|
|
|
|
//'sub_appid'=>env('WX_APP_ID'),
|
|
|
|
|
'acct'=>$orderInfo['openid'],//openid
|
|
|
|
|
'signtype' => 'RSA',
|
|
|
|
|
// 'sign' => '', // 可有可无,下面会排除
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 商户私钥(注意:需使用 PKCS#8 格式)
|
|
|
|
|
$privateKey=$this->PrivatedKey(self::$key);
|
|
|
|
|
$sign = $this->rsaSign($data, $privateKey, 'UTF-8');
|
|
|
|
|
$data['sign'] = $sign;
|
|
|
|
|
Log::info('支付请求参数', $data);
|
|
|
|
|
$response=Http::asForm()->post(self::$PayUrl,$data);
|
|
|
|
|
if ($response->successful()) {
|
|
|
|
|
$res = $response->json();
|
|
|
|
|
$check=self::ValidSign($res);
|
|
|
|
|
if($check===1){
|
|
|
|
|
$payInfo=json_decode($res['payinfo'],true);
|
|
|
|
|
|
|
|
|
|
$wc_chat_pay = new WeChatPay();
|
|
|
|
|
$wc_chat_pay->out_trade_no = $orderInfo['show'];
|
|
|
|
|
$wc_chat_pay->post_data = json_encode($data, JSON_UNESCAPED_UNICODE);
|
|
|
|
|
$wc_chat_pay->params = json_encode($payInfo, JSON_UNESCAPED_UNICODE);
|
|
|
|
|
$wc_chat_pay->save();
|
|
|
|
|
|
|
|
|
|
return $payInfo;
|
|
|
|
|
}else{
|
|
|
|
|
Yo::error_echo(999999, ['验证接口返回签名失败']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
Yo::error_echo(999999, ['请求失败']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
public function PayCheck($orderNumber)
|
|
|
|
|
{
|
|
|
|
|
$data = [
|
|
|
|
|
'appid' => self::$appid,
|
|
|
|
|
'cusid' => self::$cusid,
|
|
|
|
|
'version'=>11,
|
|
|
|
|
'reqsn'=> $orderNumber,//订单号
|
|
|
|
|
'randomstr'=>bin2hex(random_bytes(32 / 2)),
|
|
|
|
|
'signtype' => 'RSA',
|
|
|
|
|
// 'sign' => '', // 可有可无,下面会排除
|
|
|
|
|
];
|
|
|
|
|
$privateKey=$this->PrivatedKey(self::$key);
|
|
|
|
|
$sign = $this->rsaSign($data, $privateKey, 'UTF-8');
|
|
|
|
|
$data['sign'] = $sign;
|
|
|
|
|
$response=Http::asForm()->post(self::$PayCheckUrl,$data);
|
|
|
|
|
if ($response->successful()) {
|
|
|
|
|
$res = $response->json();
|
|
|
|
|
$check=self::ValidSign($res);
|
|
|
|
|
if($check===1){
|
|
|
|
|
return $res;
|
|
|
|
|
}else{
|
|
|
|
|
Yo::error_echo(999999, ['验证接口返回签名失败']);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
Yo::error_echo(999999, ['请求失败']);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public function CallBack($inBodyArray)
|
|
|
|
|
{
|
|
|
|
|
Log::info('通联支付回调参数:', $inBodyArray);
|
|
|
|
|
if(!empty($inBodyArray)){
|
|
|
|
|
$check=self::ValidSign($inBodyArray);
|
|
|
|
|
if($check===1){
|
|
|
|
|
return ['status'=>true,'body'=>$inBodyArray];
|
|
|
|
|
}else{
|
|
|
|
|
Log::info('通联支付回调参数,验签失败');
|
|
|
|
|
return ['status'=>false,'msg'=>'验签失败'];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
public function Refund($orderInfo)
|
|
|
|
|
{
|
|
|
|
|
$data = [
|
|
|
|
|
'appid' => self::$appid,
|
|
|
|
|
'cusid' => self::$cusid,
|
|
|
|
|
'version'=>11,
|
|
|
|
|
'reqsn'=> $orderInfo['show'],//订单号
|
|
|
|
|
'randomstr'=>bin2hex(random_bytes(32 / 2)),
|
|
|
|
|
'trxamt'=>$orderInfo['true_price'],//交易金额
|
|
|
|
|
'oldtrxid'=>$orderInfo['transaction'],//原交易流水号
|
|
|
|
|
'signtype' => 'RSA',
|
|
|
|
|
// 'sign' => '', // 可有可无,下面会排除
|
|
|
|
|
];
|
|
|
|
|
$privateKey=$this->PrivatedKey(self::$key);
|
|
|
|
|
$sign = $this->rsaSign($data, $privateKey, 'UTF-8');
|
|
|
|
|
$data['sign'] = $sign;
|
|
|
|
|
$response=Http::asForm()->post(self::$RefundUrl,$data);
|
|
|
|
|
if ($response->successful()) {
|
|
|
|
|
$res = $response->json();
|
|
|
|
|
$check=self::ValidSign($res);
|
|
|
|
|
if($check===1){
|
|
|
|
|
return $res;
|
|
|
|
|
}else{
|
|
|
|
|
Yo::error_echo(999999, ['退款失败,验证接口返回签名失败']);
|
|
|
|
|
}
|
|
|
|
|
}else{
|
|
|
|
|
Yo::error_echo(999999, ['请求失败']);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* RSA with SHA1 签名
|
|
|
|
|
*
|
|
|
|
|
* @param array $data 要签名的参数(自动排除空值和 sign 字段)
|
|
|
|
|
* @param string $privateKey RSA私钥(PEM格式)
|
|
|
|
|
* @param string $charset 字符编码
|
|
|
|
|
* @return string Base64编码的签名值
|
|
|
|
|
*/
|
|
|
|
|
private function rsaSign(array $data, string $privateKey, string $charset = 'UTF-8'): string
|
|
|
|
|
{
|
|
|
|
|
// 1. 过滤掉 sign 字段和空值字段
|
|
|
|
|
$filtered = array_filter($data, function ($value, $key) {
|
|
|
|
|
return $key !== 'sign' && $value !== null && $value !== '';
|
|
|
|
|
}, ARRAY_FILTER_USE_BOTH);
|
|
|
|
|
|
|
|
|
|
// 2. 按 key 的 ASCII 升序排序
|
|
|
|
|
ksort($filtered);
|
|
|
|
|
|
|
|
|
|
// 3. 拼接成 key=value&key=value 字符串
|
|
|
|
|
$stringToSign = http_build_query($filtered, '', '&', PHP_QUERY_RFC3986);
|
|
|
|
|
// 注意:http_build_query 默认使用 UTF-8,并且会 urlencode,但有些场景要求不 urlencode
|
|
|
|
|
// 如果不需要 urlencode,使用下面方式:
|
|
|
|
|
|
|
|
|
|
// 手动拼接(不进行 urlencode)
|
|
|
|
|
$pairs = [];
|
|
|
|
|
foreach ($filtered as $key => $value) {
|
|
|
|
|
$pairs[] = "$key=$value";
|
|
|
|
|
}
|
|
|
|
|
$stringToSign = implode('&', $pairs);
|
|
|
|
|
// 转为指定编码
|
|
|
|
|
$stringToSign = mb_convert_encoding($stringToSign, $charset, 'auto');
|
|
|
|
|
|
|
|
|
|
// 4. 使用 openssl_sign 进行 SHA1WithRSA 签名
|
|
|
|
|
$res = openssl_pkey_get_private($privateKey);
|
|
|
|
|
if (!$res) {
|
|
|
|
|
throw new \Exception('Invalid private key.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
openssl_sign($stringToSign, $signature, $res, OPENSSL_ALGO_SHA1);
|
|
|
|
|
openssl_free_key($res);
|
|
|
|
|
|
|
|
|
|
// 5. Base64 编码
|
|
|
|
|
return base64_encode($signature);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//验签
|
|
|
|
|
public static function ValidSign(array $array){
|
|
|
|
|
Log::info('验签:', $array);
|
|
|
|
|
$sign =$array['sign'];
|
|
|
|
|
unset($array['sign']);
|
|
|
|
|
ksort($array);
|
|
|
|
|
$bufSignSrc = self::ToUrlParams($array);
|
|
|
|
|
$public_key='MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCm9OV6zH5DYH/ZnAVYHscEELdCNfNTHGuBv1nYYEY9FrOzE0/4kLl9f7Y9dkWHlc2ocDwbrFSm0Vqz0q2rJPxXUYBCQl5yW3jzuKSXif7q1yOwkFVtJXvuhf5WRy+1X5FOFoMvS7538No0RpnLzmNi3ktmiqmhpcY/1pmt20FHQQIDAQAB';
|
|
|
|
|
$public_key = chunk_split($public_key , 64, "\n");
|
|
|
|
|
$key = "-----BEGIN PUBLIC KEY-----\n$public_key-----END PUBLIC KEY-----\n";
|
|
|
|
|
$result= openssl_verify($bufSignSrc,base64_decode($sign), $key );
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
public static function ToUrlParams(array $array)
|
|
|
|
|
{
|
|
|
|
|
$buff = "";
|
|
|
|
|
foreach ($array as $k => $v)
|
|
|
|
|
{
|
|
|
|
|
if($v != "" && !is_array($v)){
|
|
|
|
|
$buff .= $k . "=" . $v . "&";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$buff = trim($buff, "&");
|
|
|
|
|
return $buff;
|
|
|
|
|
}
|
|
|
|
|
}
|