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.
229 lines
7.0 KiB
PHP
229 lines
7.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\WeChatPay;
|
|
use Illuminate\Http\Request;
|
|
use WeChatPay\Builder;
|
|
use WeChatPay\Crypto\AesGcm;
|
|
use WeChatPay\Crypto\Rsa;
|
|
use WeChatPay\Formatter;
|
|
use WeChatPay\Util\PemUtil;
|
|
use Yo;
|
|
|
|
class WeChatPayController extends Controller
|
|
{
|
|
public static $mp_instance = false;
|
|
public static $mp_config = false;
|
|
|
|
public function callback($input, $header, $apiv3Key, $pem)
|
|
{
|
|
$inWechatpaySignature = $header['wechatpay-signature'][0];
|
|
$inWechatpayTimestamp = $header['wechatpay-timestamp'][0];
|
|
$inWechatpaySerial = $header['wechatpay-serial'][0];
|
|
$inWechatpayNonce = $header['wechatpay-nonce'][0];
|
|
$inBody = $input;
|
|
$platformPublicKeyInstance = Rsa::from($pem, Rsa::KEY_TYPE_PUBLIC);
|
|
$timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
|
|
// $verifiedStatus = Rsa::verify(
|
|
// Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
|
|
// $inWechatpaySignature,
|
|
// $platformPublicKeyInstance
|
|
// );
|
|
// if ($timeOffsetStatus && $verifiedStatus) {
|
|
if ($timeOffsetStatus) {
|
|
$inBodyArray = (array)json_decode($inBody, true);
|
|
['resource' => [
|
|
'ciphertext' => $ciphertext,
|
|
'nonce' => $nonce,
|
|
'associated_data' => $aad
|
|
]] = $inBodyArray;
|
|
$inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
|
|
return (array)json_decode($inBodyResource, true);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function builder($config)
|
|
{
|
|
self::$mp_config = $config;
|
|
$merchantPrivateKeyFilePath = 'file://' . self::$mp_config['pem_path'];
|
|
$platformCertificateFilePath = 'file://' . self::$mp_config['cer_path'];
|
|
$merchantId = self::$mp_config['mchid'];
|
|
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
|
|
self::$mp_config['pem_key'] = $merchantPrivateKeyInstance;
|
|
$merchantCertificateSerial = self::$mp_config['cer_num'];
|
|
$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
|
|
$platformCertificateSerial = self::$mp_config['v3'];
|
|
self::$mp_instance = Builder::factory([
|
|
'mchid' => $merchantId,
|
|
'serial' => $merchantCertificateSerial,
|
|
'privateKey' => $merchantPrivateKeyInstance,
|
|
'certs' => [
|
|
$platformCertificateSerial => $platformPublicKeyInstance,
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function refund($config)
|
|
{
|
|
$res = false;
|
|
try {
|
|
$resp = self::$mp_instance
|
|
->v3->refund->domestic->refunds
|
|
->post([
|
|
'json' => [
|
|
'transaction_id' => $config['transaction_id'],
|
|
'out_refund_no' => $config['out_refund_no'],
|
|
'amount' => [
|
|
'refund' => $config['total'],
|
|
'total' => $config['total'],
|
|
'currency' => 'CNY',
|
|
],
|
|
],
|
|
]);
|
|
$res = json_decode($resp->getBody(), true);
|
|
} catch (\Exception $e) {
|
|
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
|
|
$r = $e->getResponse();
|
|
$res = json_decode($r->getBody(), true);
|
|
}
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
public function create($config)
|
|
{
|
|
$res = false;
|
|
try {
|
|
$post_data = [
|
|
'appid' => self::$mp_config['appid'],
|
|
'mchid' => self::$mp_config['mchid'],
|
|
'description' => $config['description'],
|
|
'out_trade_no' => $config['out_trade_no'],
|
|
'notify_url' => $config['notify_url'],
|
|
'amount' => [
|
|
'total' => $config['total'],
|
|
],
|
|
'payer' => [
|
|
'openid' => $config['openid']
|
|
],
|
|
'settle_info' => [
|
|
'profit_sharing' => $config['profit_sharing'],
|
|
]
|
|
];
|
|
$resp = self::$mp_instance
|
|
->v3->pay->transactions->jsapi
|
|
->post([
|
|
'json' => $post_data,
|
|
]);
|
|
$res = json_decode($resp->getBody(), true);
|
|
} catch (\Exception $e) {
|
|
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
|
|
$r = $e->getResponse();
|
|
$res = json_decode($r->getBody(), true);
|
|
}
|
|
}
|
|
$params = [
|
|
'appId' => self::$mp_config['appid'],
|
|
'timeStamp' => (string)time(),
|
|
'nonceStr' => self::nonce(),
|
|
'package' => 'prepay_id=' . $res['prepay_id'],
|
|
];
|
|
$params += ['paySign' => Rsa::sign(
|
|
Formatter::joinedByLineFeed(...array_values($params)),
|
|
self::$mp_config['pem_key']
|
|
), 'signType' => 'RSA'];
|
|
$wc_chat_pay = new WeChatPay();
|
|
$wc_chat_pay->out_trade_no = $config['out_trade_no'];
|
|
$wc_chat_pay->post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
|
|
$wc_chat_pay->params = json_encode($params, JSON_UNESCAPED_UNICODE);
|
|
$wc_chat_pay->save();
|
|
return [
|
|
'appid' => $params['appId'],
|
|
'timestamp' => $params['timeStamp'],
|
|
'nonce_str' => $params['nonceStr'],
|
|
'package' => $params['package'],
|
|
'pay_sign' => $params['paySign'],
|
|
'sign_type' => $params['signType'],
|
|
];
|
|
}
|
|
|
|
public function profitsharing($sharing_data)
|
|
{
|
|
$res = false;
|
|
try {
|
|
$post_data = [
|
|
'appid' => self::$mp_config['appid'],
|
|
'transaction_id' => $sharing_data['transaction_id'],
|
|
'out_order_no' => 'P' . $sharing_data['transaction_id'],
|
|
'receivers' => $sharing_data['receivers'],
|
|
'unfreeze_unsplit' => true
|
|
];
|
|
$resp = self::$mp_instance
|
|
->v3->profitsharing->orders
|
|
->post([
|
|
'json' => $post_data,
|
|
]);
|
|
$res = json_decode($resp->getBody(), true);
|
|
} catch (\Exception $e) {
|
|
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
|
|
$r = $e->getResponse();
|
|
$res = json_decode($r->getBody(), true);
|
|
}
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
public function profitsharing_return($return_data)
|
|
{
|
|
$res = false;
|
|
try {
|
|
$resp = self::$mp_instance
|
|
->v3->profitsharing->returnOrders
|
|
->post([
|
|
'json' => $return_data,
|
|
]);
|
|
$res = json_decode($resp->getBody(), true);
|
|
} catch (\Exception $e) {
|
|
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
|
|
$r = $e->getResponse();
|
|
$res = json_decode($r->getBody(), true);
|
|
}
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
public function check($out_trade_no)
|
|
{
|
|
$res = false;
|
|
try {
|
|
$resp = self::$mp_instance
|
|
->v3->pay->transactions->outTradeNo->_out_trade_no_
|
|
->get([
|
|
'query' => ['mchid' => self::$mp_config['mchid']],
|
|
'out_trade_no' => (string)$out_trade_no,
|
|
]);
|
|
$res = json_decode($resp->getBody(), true);
|
|
} catch (\Exception $e) {
|
|
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
|
|
$r = $e->getResponse();
|
|
$res = json_decode($r->getBody(), true);
|
|
}
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
public static function nonce($l = 16)
|
|
{
|
|
$charts = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz0123456789";
|
|
$max = strlen($charts) - 1;
|
|
$noncestr = "";
|
|
for ($i = 0; $i < $l; $i++) {
|
|
$noncestr .= $charts[rand(0, $max)];
|
|
}
|
|
return $noncestr;
|
|
}
|
|
}
|