You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
9.4 KiB
PHP

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?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;
}
}