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