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

282 lines
7.1 KiB

<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace crmeb\services\pay\extend\allinpay;
use crmeb\exceptions\ApiException;
use crmeb\services\HttpService;
use think\facade\Log;
/**
* Class Client
* @author 等风来
* @email 136327134@qq.com
* @date 2022/12/27
* @package crmeb\services\pay\extend\allinpay
*/
class Client
{
//生产地址
const API_URL = 'https://vsp.allinpay.com/apiweb/';
//测试接口地址
const BETA_API_URL = 'https://syb-test.allinpay.com/apiweb/';
//版本号
const VERSION_NUM_11 = '11';
//版本号
const VERSION_NUM_12 = '12';
protected $signType = 'MD5';
/**
* @var string
*/
protected $cusid = '';
/**
* @var string
*/
protected $appid = '';
/**
* @var string
*/
protected $privateKey = '';
/**
* @var
*/
protected $publicKey = '';
/**
* 回调地址
* @var string
*/
protected $notifyUrl = '';
/**
* 是否测试
* @var bool
*/
protected $isBeta = true;
/**
* debug模式
* @var bool
*/
protected $isDebug = true;
/**
* Client constructor.
* @param array $config
*/
public function __construct(array $config = [])
{
$this->appid = $config['appid'] ?? '';
$this->cusid = $config['cusid'] ?? '';
$this->privateKey = $config['privateKey'] ?? '';
$this->publicKey = $config['publicKey'] ?? '';
$this->notifyUrl = $config['notifyUrl'] ?? '';
$this->isBeta = $config['isBeta'] ?? true;
}
/**
* 发送请求
* @param string $url
* @param array $options
* @return mixed
*/
public function send(string $url, array $options = [])
{
$data = $options['data'] ?? [];
$header = $options['header'] ?? [];
$data['cusid'] = $this->cusid;
$data['appid'] = $this->appid;
if (!isset($data['version'])) {
$data['version'] = self::VERSION_NUM_11;
}
$data['signtype'] = $this->signType;
$data['randomstr'] = uniqid();
if (!empty($options['form'])) {
$data['charset'] = 'UTF-8';
$data['version'] = self::VERSION_NUM_12;
$data['sign'] = $this->sign($data);
return $data;
}
$data['sign'] = $this->sign($data);
$response = $this->request($url, $data, 'post', $header);
if ('SUCCESS' !== $response['retcode']) {
throw new ApiException($response['retmsg']);
}
if (!empty($response['trxstatus']) && !in_array($response['trxstatus'], ['0000', '2008', '2000'])) {
throw new ApiException($response['errmsg']);
}
if ($this->validSign($response)) {
return $response;
}
throw new ApiException('创建订单成功验签失败');
}
/**
* @param string $url
* @param array $data
* @param string $method
* @param array $header
* @param int $timeout
* @return mixed
*/
public function request(string $url, array $data = [], string $method = 'post', array $header = [], int $timeout = 10)
{
$headerData = [];
if ($header) {
foreach ($header as $key => $item) {
$headerData[] = $key . ':' . $item;
}
}
$content = HttpService::request($this->baseUrl($url), $method, $data, $headerData, $timeout);
$respones = json_decode($content, true, 512, JSON_BIGINT_AS_STRING);
$this->debugLog('API response decoded:', ['content' => $respones, 'header' => $header]);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new ApiException('Failed to parse JSON: ' . json_last_error_msg());
}
return $respones;
}
/**
* @param string $message
* @param array $contents
*/
protected function debugLog(string $message, array $contents = [])
{
$this->isDebug && Log::debug($message, $contents);
}
/**
* @param string|null $url
* @return string
*/
protected function baseUrl(string $url = null)
{
$baseUrl = $this->isBeta ? self::BETA_API_URL : self::API_URL;
if ($url) {
$baseUrl .= $url;
}
return $baseUrl;
}
/**
* @param array $data
* @return string
*/
public function sign(array $data)
{
$private_key = $this->privateKey;
if ($this->signType === 'MD5') {
$data['key'] = $private_key;
ksort($data);
$bufSignSrc = $this->toUrlParams($data);
return md5($bufSignSrc);
} else {
ksort($data);
$bufSignSrc = $this->toUrlParams($data);
$private_key = chunk_split($private_key, 64, "\n");
$key = "-----BEGIN RSA PRIVATE KEY-----\n" . wordwrap($private_key) . "-----END RSA PRIVATE KEY-----";
openssl_sign($bufSignSrc, $signature, $key);
$sign = base64_encode($signature);//加密后的内容通常含有特殊字符,需要编码转换下,在网络间通过url传输时要注意base64编码是否是url安全的
return $sign;
}
}
/**
* @param array $data
* @return string
*/
public function toUrlParams(array $data)
{
$buff = "";
foreach ($data as $k => $v) {
if ($v != "" && !is_array($v)) {
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
/**
* @param array $data
* @return false|int
*/
public function validSign(array $data)
{
$sign = $data['sign'];
unset($data['sign']);
if ($this->signType === 'MD5') {
$data['key'] = $this->privateKey;
ksort($data);
$bufSignSrc = $this->toUrlParams($data);
return strtolower($sign) == strtolower(md5($bufSignSrc));
} else {
ksort($data);
$bufSignSrc = $this->toUrlParams($data);
$public_key = $this->publicKey;
$public_key = chunk_split($public_key, 64, "\n");
$key = "-----BEGIN PUBLIC KEY-----\n$public_key-----END PUBLIC KEY-----\n";
return openssl_verify($bufSignSrc, base64_decode($sign), $key);
}
}
/**
* @param string $notifyUrl
* @return $this
* @author 等风来
* @email 136327134@qq.com
* @date 2023/2/7
*/
public function setNotifyUrl(string $notifyUrl)
{
$this->notifyUrl = $notifyUrl;
return $this;
}
}