Files
ProxyPanel/app/Utils/Library/AlipayF2F.php
BrettonYe d9ff6ad6ee Improve System Blade
- Removed Task config, add those settings to System blade.
- Improve the font-end of system setting blade.
- Improve Payments files to be more  "Modularize"
2025-06-09 23:54:12 +08:00

202 lines
6.2 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/*
* 作者BrettonYe
* 功能ProxyPanel 支付宝面对面【收单线下交易预创建】【收单交易查询】接口实现库
* 更新时间2023/10/29
* 参考资料https://opendocs.alipay.com/open/02ekfg?scene=19 riverslei/payment
*/
namespace App\Utils\Library;
use Illuminate\Support\Facades\Http;
use RuntimeException;
class AlipayF2F
{
private static string $gatewayUrl = 'https://openapi.alipay.com/gateway.do'; // https://openapi-sandbox.dl.alipaydev.com/gateway.do
private array $config;
public function __construct(array $rawConfig)
{
$config = [
'app_id' => $rawConfig['app_id'],
'public_key' => '',
'private_key' => '',
'notify_url' => $rawConfig['notify_url'],
];
if ($rawConfig['ali_public_key']) {
$config['public_key'] = self::getRsaKeyValue($rawConfig['ali_public_key'], false);
}
if (empty($config['public_key'])) {
throw new RuntimeException('please set ali public key');
}
// 初始 RSA私钥文件 需要检查该文件是否存在
if ($rawConfig['rsa_private_key']) {
$config['private_key'] = self::getRsaKeyValue($rawConfig['rsa_private_key']);
}
if (empty($config['private_key'])) {
throw new RuntimeException('please set ali private key');
}
$this->config = $config;
}
/**
* 获取rsa密钥内容.
*
* @param string $key 传入的密钥信息, 可能是文件或者字符串
* @param bool $is_private 私钥/公钥
*/
public static function getRsaKeyValue(string $key, bool $is_private = true): ?string
{
$keyStr = is_file($key) ? @file_get_contents($key) : $key;
if (empty($keyStr)) {
return null;
}
$keyStr = str_replace('\n', '', $keyStr);
// 为了解决用户传入的密钥格式,这里进行统一处理
if ($is_private) {
$beginStr = "-----BEGIN RSA PRIVATE KEY-----\n";
$endStr = "\n-----END RSA PRIVATE KEY-----";
} else {
$beginStr = "-----BEGIN PUBLIC KEY-----\n";
$endStr = "\n-----END PUBLIC KEY-----";
}
return $beginStr.wordwrap($keyStr, 64, "\n", true).$endStr;
}
public function tradeQuery(array $content): array
{
$this->setMethod('alipay.trade.query');
$this->setContent($content);
return $this->send();
}
private function setMethod(string $method): void
{
$this->config['method'] = $method;
}
private function setContent(array $content): void
{
$content = array_filter($content);
ksort($content);
$this->config['biz_content'] = json_encode($content);
}
private function send(): array
{
$response = Http::timeout(15)->retry(2)->get(self::$gatewayUrl, $this->buildParams())->json();
$resKey = str_replace('.', '_', $this->config['method']).'_response';
if (! isset($response[$resKey])) {
throw new RuntimeException('请求错误-看起来是请求失败');
}
if (! $this->validate_response_sign($response[$resKey], $response['sign'])) {
throw new RuntimeException('验签错误-'.$response[$resKey]['msg'].' | '.($response[$resKey]['sub_msg'] ?? var_export($response, true)));
}
$response = $response[$resKey];
if ($response['msg'] !== 'Success') {
throw new RuntimeException($response[$resKey]['sub_msg'] ?? var_export($response, true));
}
return $response;
}
private function buildParams(): array
{
$params = [
'app_id' => $this->config['app_id'],
'method' => $this->config['method'],
'charset' => 'utf-8',
'sign_type' => 'RSA2',
'timestamp' => date('Y-m-d H:m:s'),
'biz_content' => $this->config['biz_content'],
'version' => '1.0',
'notify_url' => $this->config['notify_url'],
];
$params = array_filter($params);
$params['sign'] = $this->encrypt($this->buildQuery($params));
return $params;
}
/**
* RSA2签名.
*
* @param string $data 签名的数组
*
* @throws RuntimeException
*/
private function encrypt(string $data): string
{
$privateKey = openssl_pkey_get_private($this->config['private_key']); // 私钥
if (empty($privateKey)) {
throw new RuntimeException('您使用的私钥格式错误请检查RSA私钥配置');
}
openssl_sign($data, $sign, $privateKey, OPENSSL_ALGO_SHA256);
return base64_encode($sign); // base64编码
}
private function buildQuery(array $params): string
{
ksort($params); // 排序
return urldecode(http_build_query($params)); // 组合
}
/**
* 同步返回验签.
*
* @param array $body 待签名数据
*
* @throws RuntimeException
*/
public function validate_response_sign(array $body, string $sign): bool
{
unset($body['sign'], $body['sign_type']);
$publicKey = openssl_pkey_get_public($this->config['public_key']);
if (empty($publicKey)) {
throw new RuntimeException('支付宝RSA公钥错误。请检查公钥文件格式是否正确');
}
return (bool) openssl_verify(json_encode($body), base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
}
/**
* 异步通知验签.
*
* @param array $body 待签名数据
*
* @throws RuntimeException
*/
public function validate_notification_sign(array $body, string $sign): bool
{
unset($body['sign'], $body['sign_type']);
$publicKey = openssl_pkey_get_public($this->config['public_key']);
if (empty($publicKey)) {
throw new RuntimeException('支付宝RSA公钥错误。请检查公钥文件格式是否正确');
}
return (bool) openssl_verify($this->buildQuery($body), base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
}
public function qrCharge(array $content): array
{
$this->setMethod('alipay.trade.precreate');
$this->setContent($content);
return $this->send();
}
}