transfer
This commit is contained in:
parent
4e24c65aa4
commit
8f71d6b80f
@ -7,6 +7,7 @@ use App\Http\Response\ResponseJson;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Log;
|
||||
use TypeError;
|
||||
|
||||
class WechatPayController extends Controller
|
||||
@ -19,7 +20,8 @@ class WechatPayController extends Controller
|
||||
"scene_id" => "string",
|
||||
"openid" => "required|string",
|
||||
"amount" => "required|integer",
|
||||
"remark" => "required|string"
|
||||
"remark" => "required|string",
|
||||
"notify_url" => "required|url"
|
||||
];
|
||||
$input = $request->all();
|
||||
$validator = Validator::make($input, $rules, $messages = [
|
||||
@ -39,8 +41,23 @@ class WechatPayController extends Controller
|
||||
$openid = $request->openid;
|
||||
$amount = $request->amount;
|
||||
$remark = $request->remark;
|
||||
$res = WechatPayService::mchTransfer($trade_no, $scene_id, $openid, $amount, $remark, []);
|
||||
$notify_url = $request->notify_url;
|
||||
$res = WechatPayService::mchTransfer($trade_no, $scene_id, $openid, $amount, $remark, $notify_url, []);
|
||||
|
||||
return $this->success("ok", $res);
|
||||
}
|
||||
|
||||
function mchTransferCallback(Request $request)
|
||||
{
|
||||
$headers = [
|
||||
'wechatpay-timestamp' => $_SERVER['HTTP_WECHATPAY_TIMESTAMP'] ?? '',
|
||||
'wechatpay-nonce' => $_SERVER['HTTP_WECHATPAY_NONCE'] ?? '',
|
||||
'wechatpay-signature' => $_SERVER['HTTP_WECHATPAY_SIGNATURE'] ?? '',
|
||||
'wechatpay-serial' => $_SERVER['HTTP_WECHATPAY_SERIAL'] ?? '',
|
||||
];
|
||||
$body = file_get_contents('php://input');
|
||||
$res = WechatPayService::mchTransferCallback($headers, $body);
|
||||
Log::info("解密数据", ["data" => $res]);
|
||||
return $this->success("ok", $res);
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,8 +40,13 @@ class RouteServiceProvider extends ServiceProvider
|
||||
->prefix("util/api")
|
||||
->group(base_path('routes/util/api.php'));
|
||||
|
||||
|
||||
$utilPrefix = "api/wechatpay";
|
||||
if (config("app.env") == "local") {
|
||||
$utilPrefix = "util/api/wechatpay";
|
||||
}
|
||||
Route::middleware('api')
|
||||
->prefix("api/wechatpay")
|
||||
->prefix($utilPrefix)
|
||||
->group(base_path('routes/pay/wechat.php'));
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
use Illuminate\Auth\Events\Login;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use WeChatPay\Builder;
|
||||
use WeChatPay\BuilderChainable;
|
||||
use WeChatPay\Crypto\AesGcm;
|
||||
use WeChatPay\Crypto\Rsa;
|
||||
|
||||
class WechatPayService
|
||||
@ -59,7 +60,7 @@ class WechatPayService
|
||||
* 商家转账
|
||||
* @return void
|
||||
*/
|
||||
public function mchTransfer(string $trade_no, string $scene_id, string $openid, int $amount, string $remark, array $transfer_scene_report_infos = []): array
|
||||
public function mchTransfer(string $trade_no, string $scene_id, string $openid, int $amount, string $remark, string $notify_url, array $transfer_scene_report_infos = []): array
|
||||
{
|
||||
// 发送请求
|
||||
if (empty(count($transfer_scene_report_infos))) {
|
||||
@ -83,6 +84,7 @@ class WechatPayService
|
||||
"openid" => $openid,
|
||||
"transfer_amount" => $amount,
|
||||
"transfer_remark" => $remark,
|
||||
"notify_url" => $notify_url,
|
||||
"transfer_scene_report_infos" => $transfer_scene_report_infos
|
||||
];
|
||||
Log::info("转账数据", $data);
|
||||
@ -105,6 +107,96 @@ class WechatPayService
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 商家转账回调
|
||||
*/
|
||||
public function mchTransferCallback($headers, $rawBody)
|
||||
{
|
||||
if (!$this->verifySignature($headers, $rawBody)) {
|
||||
Log::error('微信回调验签失败', ['headers' => $headers]);
|
||||
return null; // 返回 500 让微信重试
|
||||
}
|
||||
|
||||
// 4. 解密回调数据
|
||||
$decryptData = $this->decryptNotifyData($rawBody);
|
||||
if (!$decryptData) {
|
||||
Log::error('数据解密失败');
|
||||
return null;
|
||||
}
|
||||
|
||||
return $decryptData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名(RSA-SHA256)
|
||||
*/
|
||||
private function verifySignature(array $headers, string $body): bool
|
||||
{
|
||||
// 检查必要头是否存在
|
||||
if (
|
||||
empty($headers['timestamp']) || empty($headers['nonce']) ||
|
||||
empty($headers['signature']) || empty($headers['serial'])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查序列号是否匹配(可选的额外校验)
|
||||
// $expectedSerial = $this->getPlatformCertSerial();
|
||||
// if ($headers['serial'] !== $expectedSerial) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// 构造验签名串:timestamp + \n + nonce + \n + body + \n
|
||||
$signStr = $headers['timestamp'] . "\n"
|
||||
. $headers['nonce'] . "\n"
|
||||
. $body . "\n";
|
||||
|
||||
// 加载微信支付平台公钥
|
||||
$publicKey = openssl_pkey_get_public('file://' . config("wechatpay.payment.platform_cert_path"));
|
||||
if (!$publicKey) {
|
||||
Log::error('加载平台证书失败');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Base64 解码签名
|
||||
$signature = base64_decode($headers['signature']);
|
||||
|
||||
// 验证签名
|
||||
$result = openssl_verify($signStr, $signature, $publicKey, OPENSSL_ALGO_SHA256);
|
||||
|
||||
return $result === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* AES-256-GCM 解密回调数据
|
||||
*/
|
||||
private function decryptNotifyData(string $rawBody): ?array
|
||||
{
|
||||
$data = json_decode($rawBody, true);
|
||||
|
||||
if (!isset($data['resource'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$resource = $data['resource'];
|
||||
$ciphertext = base64_decode($resource['ciphertext']);
|
||||
$nonce = $resource['nonce'];
|
||||
$associatedData = $resource['associated_data'] ?? '';
|
||||
|
||||
// PHP 7.1+ 原生支持 AES-256-GCM
|
||||
$decrypted = AesGcm::decrypt(
|
||||
$ciphertext, // Base64解码后的密文(包含tag)
|
||||
config("wechatpay.payment.api3_key"),
|
||||
$nonce,
|
||||
$associatedData
|
||||
);
|
||||
if ($decrypted === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return json_decode($decrypted, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商家批量转账到零钱
|
||||
*/
|
||||
|
||||
@ -4,3 +4,4 @@ use App\Http\Controllers\WechatPayController;
|
||||
|
||||
//发起转账
|
||||
Route::post('saas/mch/transfer', [WechatPayController::class, "mchTransfer"])->middleware('merchant_user');
|
||||
Route::post('saas/mch/transfer/callback', [WechatPayController::class, "mchTransferCallback"]);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user