当前位置:网站首页>PHP wechat special merchant incoming V3 packaging interface
PHP wechat special merchant incoming V3 packaging interface
2022-06-13 08:45:00 【variation8】
<?php
/**
* Wechat service provider V3
*/
class Wxpaymch
{
// error message
private $error = '';
// Merchant mchid
private $mch_id = 'xxxx';
// Merchant API v3 secret key ( Wechat service provider - Account center -API Security api v3 secret key https://pay.weixin.qq.com/index.php/core/cert/api_cert)
private $mch_api_key = 'xxxx';
// Certificate number (apiclient_cert.pem Obtained after certificate resolution )
private $serial_no = 'xxxx';
// Private key apiclient_key.pem( Wechat service provider - Account center -API Security download https://pay.weixin.qq.com/index.php/core/cert/api_cert)
private $mch_private_key = __DIR__ . DIRECTORY_SEPARATOR . 'cert' . DIRECTORY_SEPARATOR . 'apiclient_key.pem';
// Payment platform public key ( Interface acquisition )
private $public_key_path = __DIR__ . DIRECTORY_SEPARATOR . 'cert_ficates_v3.pem';
/*
* Wechat special merchant incoming interface
*/
public function subApplyment( array $params)
{
// Parameter preparation
$data = [
// Business application No
'business_code' => $this->getBusinessCode(),
// Super administrator information
'contact_info' => [
'contact_name' => $this->getEncrypt($params['contact_name']),// Super administrator name
'contact_id_number' => $this->getEncrypt($params['contact_id_number']),// Super administrator ID number
'mobile_phone' => $this->getEncrypt($params['mobile_phone']),// Contact your cell phone
'contact_email' => $this->getEncrypt($params['contact_email']),// Contact email
],
// Subject data
'subject_info' => [
// The main types of SUBJECT_TYPE_INDIVIDUAL( self-employed person )SUBJECT_TYPE_ENTERPRISE( Enterprises )SUBJECT_TYPE_INSTITUTIONS( party and government 、 Government agencies and institutions )SUBJECT_TYPE_OTHERS( Other organizations )
'subject_type' => 'SUBJECT_TYPE_INDIVIDUAL',
// operator / Corporate identity card
'identity_info' => [
'id_doc_type' => 'IDENTIFICATION_TYPE_IDCARD',// Document type
'owner' => true,// operator / Whether the legal person is the beneficiary
// ID card information
'id_card_info' => [
'id_card_copy' => 'jTpGmxUX3FBWVQ5NJTZvlKX_gdU4cRz7z5NxpnFuAxhBTEO_PvWkfSCJ3zVIn001D8daLC-ehEuo0BJqRTvDujqhThn4ReFxikqJ5YW6zFQ',
'id_card_national' => '47ZC6GC-vnrbEny__Ie_An5-tCpqxucuxi-vByf3Gjm7KE53JXvGy9tqZm2XAUf-4KGprrKhpVBDIUv0OF4wFNIO4kqg05InE4d2I6_H7I4',
'id_card_name' => $this->getEncrypt($params['id_card_name']),
'id_card_number' => $this->getEncrypt($params['id_card_number']),
'card_period_begin' => '2026-06-06',
'card_period_end' => '2036-06-06',
],
],
],
// Business information
'business_info' => [
'merchant_shortname' => $params['merchant_shortname'],
'service_phone' => $params['service_phone'],
'sales_info' => [
'sales_scenes_type' => ['SALES_SCENES_STORE'],
// Offline store scenario
'biz_store_info' => [
'biz_store_name' => $params['biz_store_name'],
'biz_address_code' => $params['biz_address_code'],
'biz_store_address' => $params['biz_store_address'],
'store_entrance_pic' => ['0P3ng6KTIW4-QJBZR9FwczhJehHhAZN6BKXQPcs-VvdSo'],
'indoor_pic' => ['0P3ng6KTIW4-Q_l2FjmFJBZR9FwczhJehHhAZN6BKXQPcs-VvdSo'],
],
],
],
// Settlement rules
'settlement_info' => [
'settlement_id' => $params['settlement_id'],
'qualification_type' => $params['qualification_type'],
'activities_id' => $params['activities_id'],
'activities_rate' => $params['activities_rate'],
],
// Settlement bank account
'bank_account_info' => [
'bank_account_type' => 'BANK_ACCOUNT_TYPE_CORPORATE',
'account_name' => $this->getEncrypt($params['account_name']),
'account_bank' => $params['account_bank'],
'bank_address_code' => $params['bank_address_code'],
'bank_name' => $params['bank_name'],
'account_number' => $this->getEncrypt($params['account_number']),
],
// Supplementary materials
'addition_info'=>[
'legal_person_commitment'=>$params['legal_person_commitment'],
'business_addition_pics'=>$params['business_addition_pics'],
],
];
$url = 'https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/';
// Obtain the payment platform certificate code ( You can also use the serial_no source :https://api.mch.weixin.qq.com/v3/certificates)
$serial_no = $this->parseSerialNo($this->getCertFicates());
$bodyData = json_encode($data);
// Get authentication information
$authorization = $this->getAuthorization($url, 'POST', $bodyData);
$header = [
'Content-Type:application/json',
'Accept:application/json',
'User-Agent:*/*',
'Authorization:' . $authorization,
'Wechatpay-Serial:' . $serial_no
];
$json = $this->getCurl('POST', $url, $bodyData, $header);
$data = json_decode($json, true);
if (isset($data['code']) && isset($data['message'])) {
$this->error = '[subApplyment] Request error code:' . $data['code'] . ' msg:' . $data['message'];
return false;
}
if (empty($applyment_id = $data['applyment_id'])) {
$this->error = '[subApplyment] Returns an error ';
return false;
}
return $applyment_id;
}
/**
* Incoming inquiry
*/
public function queryApplyment($business_code)
{
$url = 'https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/business_code/' . $business_code;
// Get authentication information
$authorization = $this->getAuthorization($url);
$header = [
'Content-Type:application/json',
'Accept:application/json',
'User-Agent:*/*',
'Authorization:' . $authorization
];
$json = $this->getCurl('GET', $url, '', $header);
$data = json_decode($json, true);
if (isset($data['code']) && isset($data['message'])) {
$this->error = '[queryApplyment] Request error code:' . $data['code'] . ' msg:' . $data['message'];
return false;
}
return $data;
}
/**
* Upload files
*/
public function mediaUpload()
{
// To upload pictures
$filename = '1.png';
$filepath = __DIR__ . '/' . $filename;
if (!file_exists($filepath)) {
$this->error = '[mediaUpload] The file could not be found ';
}
$url = 'https://api.mch.weixin.qq.com/v3/merchant/media/upload';
$fi = new \finfo(FILEINFO_MIME_TYPE);
$mime_type = $fi->file($filepath);
$meta = [
'filename' => $filename,
'sha256' => hash_file('sha256', $filepath)
];
// Get authentication information
$authorization = $this->getAuthorization($url, 'POST', json_encode($meta));
$boundary = uniqid();
$header = [
'Accept:application/json',
'User-Agent:*/*',
'Content-Type:multipart/form-data;boundary=' . $boundary,
'Authorization:' . $authorization
];
// Combination parameter
$boundaryStr = "--{$boundary}\r\n";
$out = $boundaryStr;
$out .= 'Content-Disposition: form-data; name="meta"' . "\r\n";
$out .= 'Content-Type: application/json' . "\r\n";
$out .= "\r\n";
$out .= json_encode($meta) . "\r\n";
$out .= $boundaryStr;
$out .= 'Content-Disposition: form-data; name="file"; filename="' . $filename . '"' . "\r\n";
$out .= 'Content-Type: ' . $mime_type . ';' . "\r\n";
$out .= "\r\n";
$out .= file_get_contents($filepath) . "\r\n";
$out .= "--{$boundary}--\r\n";
$json = $this->getCurl('POST', $url, $out, $header);
$data = json_decode($json, true);
if (isset($data['code']) && isset($data['message'])) {
$this->error = '[mediaUpload] Request error code:' . $data['code'] . ' msg:' . $data['message'];
return false;
}
if (empty($media_id = $data['media_id'])) {
$this->error = '[mediaUpload] Returns an error ';
return false;
}
return $media_id;
}
/**
* Obtain wechat payment platform certificate
*/
public function certFicates()
{
$url = 'https://api.mch.weixin.qq.com/v3/certificates';
// Get authentication information
$authorization = $this->getAuthorization($url);
$header = [
'Content-Type:application/json',
'Accept:application/json',
'User-Agent:*/*',
'Authorization:' . $authorization
];
$json = $this->getCurl('GET', $url, '', $header);
$data = json_decode($json, true);
if (isset($data['code']) && isset($data['message'])) {
$this->error = '[certFicates] Request error code:' . $data['code'] . ' msg:' . $data['message'];
return false;
}
if (empty($cfdata = $data['data'][0])) {
$this->error = '[certFicates] Returns an error ';
return false;
}
return $cfdata;
}
/**
* Get authentication information
* @param string $url
* @param string $http_method
* @param string $body
* @return string
* @throws Exception
*/
private function getAuthorization($url, $http_method = 'GET', $body = '')
{
if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
throw new \Exception(" At present PHP Environment not supported SHA256withRSA");
}
// Private key address
$mch_private_key = $this->mch_private_key;
// Merchant number
$merchant_id = $this->mch_id;
// Current timestamp
$timestamp = time();
// Random string
$nonce = $this->getNonceStr();
// Certificate number
$serial_no = $this->serial_no;
$url_parts = parse_url($url);
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
$message = $http_method . "\n" .
$canonical_url . "\n" .
$timestamp . "\n" .
$nonce . "\n" .
$body . "\n";
openssl_sign($message, $raw_sign, \openssl_get_privatekey(\file_get_contents($mch_private_key)), 'sha256WithRSAEncryption');
$sign = base64_encode($raw_sign);
$schema = 'WECHATPAY2-SHA256-RSA2048';
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
$merchant_id, $nonce, $timestamp, $serial_no, $sign);
return $schema . ' ' . $token;
}
/**
* Sensitive character encryption
* @param $str
* @return string
* @throws Exception
*/
private function getEncrypt($str)
{
static $content;
if (empty($content)) {
$content = $this->getCertFicates();
}
$encrypted = '';
if (openssl_public_encrypt($str, $encrypted, $content, OPENSSL_PKCS1_OAEP_PADDING)) {
//base64 code
$sign = base64_encode($encrypted);
}
else {
throw new \Exception('encrypt failed');
}
return $sign;
}
/**
* Obtain the payment platform certificate
* @return false|string
*/
private function getCertFicates()
{
$public_key_path = $this->public_key_path;
if (!file_exists($public_key_path)) {
$cfData = $this->certFicates();
$content = $this->decryptToString($cfData['encrypt_certificate']['associated_data'], $cfData['encrypt_certificate']['nonce'], $cfData['encrypt_certificate']['ciphertext'], $this->mch_api_key);
file_put_contents($public_key_path, $content);
}
else {
$content = file_get_contents($public_key_path);
}
return $content;
}
/**
* Business number
* @return string
*/
private function getBusinessCode()
{
return date('Ymd') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
}
/**
* Random string
* @param int $length
* @return string
*/
private function getNonceStr($length = 16)
{
// Password character set , You can add any character you want
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $str;
}
/**
* @param string $method
* @param string $url
* @param array|string $data
* @param array $headers
* @param int $timeout
* @return bool|string
*/
private function getCurl($method = 'GET', $url, $data, $headers = [], $timeout = 10)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
if (!empty($headers)) {
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
}
if ($method == 'POST') {
curl_setopt($curl, CURLOPT_POST, TRUE);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
else {
}
$result = curl_exec($curl);
curl_close($curl);
return $result;
}
/**
* Decrypt AEAD_AES_256_GCM ciphertext( Official case - Transformed )
*
* @param string $associatedData AES GCM additional authentication data
* @param string $nonceStr AES GCM nonce
* @param string $ciphertext AES GCM cipher text
*
* @return string|bool Decrypted string on success or FALSE on failure
*/
private function decryptToString($associatedData, $nonceStr, $ciphertext, $aesKey)
{
$auth_tag_length_byte = 16;
$ciphertext = \base64_decode($ciphertext);
if (strlen($ciphertext) <= $auth_tag_length_byte) {
return false;
}
// ext-sodium (default installed on >= PHP 7.2)
if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') &&
\sodium_crypto_aead_aes256gcm_is_available()) {
return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey);
}
// ext-libsodium (need install libsodium-php 1.x via pecl)
if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') &&
\Sodium\crypto_aead_aes256gcm_is_available()) {
return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey);
}
// openssl (PHP >= 7.1 support AEAD)
if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
$ctext = substr($ciphertext, 0, -$auth_tag_length_byte);
$authTag = substr($ciphertext, -$auth_tag_length_byte);
return \openssl_decrypt($ctext, 'aes-256-gcm', $aesKey, \OPENSSL_RAW_DATA, $nonceStr,
$authTag, $associatedData);
}
throw new \Exception('AEAD_AES_256_GCM need PHP 7.1 Above or installed libsodium-php');
}
/**
* Get certificate number ( Official case - Transformed )
* @param $certificate
* @return string
*/
private function parseSerialNo($certificate)
{
$info = \openssl_x509_parse($certificate);
if (!isset($info['serialNumber']) && !isset($info['serialNumberHex'])) {
throw new \InvalidArgumentException(' Certificate format error ');
}
$serialNo = '';
// PHP 7.0+ provides serialNumberHex field
if (isset($info['serialNumberHex'])) {
$serialNo = $info['serialNumberHex'];
}
else {
// PHP use i2s_ASN1_INTEGER in openssl to convert serial number to string,
// i2s_ASN1_INTEGER may produce decimal or hexadecimal format,
// depending on the version of openssl and length of data.
if (\strtolower(\substr($info['serialNumber'], 0, 2)) == '0x') { // HEX format
$serialNo = \substr($info['serialNumber'], 2);
}
else { // DEC format
$value = $info['serialNumber'];
$hexvalues = ['0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
while ($value != '0') {
$serialNo = $hexvalues[\bcmod($value, '16')] . $serialNo;
$value = \bcdiv($value, '16', 0);
}
}
}
return \strtoupper($serialNo);
}
public function getError()
{
return $this->error;
}
}
边栏推荐
- Custom exception class myexception
- Verify the word limit of textarea input box. Only prompt but no submission limit
- 淘宝商品销量接口/淘宝商品销量监控接口/商品累计销量接口
- Phpexcel 10008 error resolution
- Redis subscribe connection timeout interrupt problem solution
- [notes] like the solution to the problem of slow query (index + explicitly specifying query fields)
- GBase 8a磁盘问题及处理
- filebeat采集日志到ELK
- Remote access and control
- Docker installing MySQL local remote connection docker container MySQL
猜你喜欢
Mapbox usage, including drawing, loading, modifying, deleting points and faces, displaying pop ups, etc
Guidance process and service control
MySQL queries difference sets (missing data) by linking tables based on an associated field
Vscode define code block -- define cursor position
MySQL sorts according to the specified order of the specified fields
anaconda下安装pytorch
Browser render passes
Uni app subcontracting loading and optimization
Buuctf web (VII)
Wechat upload picture material interface
随机推荐
Dest0g3 520迎新賽
[leetcode weekly race record] record of the 80th biweekly race
4. Relationship selector (parent-child relationship, ancestor offspring relationship, brother relationship)
JS array method
In order to resist the flood, the soldiers have been fighting for 89 hours. How many days and hours are there in total?
PHP isset() method ignores data error handling caused by null parameter value
Request alarm: refer policy: strict origin when cross origin or reference site policy: no refer when downgrade
[notes] like the solution to the problem of slow query (index + explicitly specifying query fields)
Differences and uses among cookies, localstorage, sessionstorage, and application caching
關於RSA加密解密原理
Buuctf web (IV)
WARNING:tornado.access:404 GET /favicon.ico (172.16.8.1) 1.84ms [附静态文件设置]
Browser render passes
Invalid flex layout setting width
顺时针打印个数组
容器概念和云原生
How to save the video of wechat video number locally?
Numeric types in SQL. Try to avoid using null as the default value
Uni app essay
Wrap dynamically created child elements in dynamically created structures