transfer
This commit is contained in:
parent
4e24c65aa4
commit
8f71d6b80f
@ -7,6 +7,7 @@ use App\Http\Response\ResponseJson;
|
|||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Log;
|
||||||
use TypeError;
|
use TypeError;
|
||||||
|
|
||||||
class WechatPayController extends Controller
|
class WechatPayController extends Controller
|
||||||
@ -19,7 +20,8 @@ class WechatPayController extends Controller
|
|||||||
"scene_id" => "string",
|
"scene_id" => "string",
|
||||||
"openid" => "required|string",
|
"openid" => "required|string",
|
||||||
"amount" => "required|integer",
|
"amount" => "required|integer",
|
||||||
"remark" => "required|string"
|
"remark" => "required|string",
|
||||||
|
"notify_url" => "required|url"
|
||||||
];
|
];
|
||||||
$input = $request->all();
|
$input = $request->all();
|
||||||
$validator = Validator::make($input, $rules, $messages = [
|
$validator = Validator::make($input, $rules, $messages = [
|
||||||
@ -39,8 +41,23 @@ class WechatPayController extends Controller
|
|||||||
$openid = $request->openid;
|
$openid = $request->openid;
|
||||||
$amount = $request->amount;
|
$amount = $request->amount;
|
||||||
$remark = $request->remark;
|
$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);
|
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")
|
->prefix("util/api")
|
||||||
->group(base_path('routes/util/api.php'));
|
->group(base_path('routes/util/api.php'));
|
||||||
|
|
||||||
|
|
||||||
|
$utilPrefix = "api/wechatpay";
|
||||||
|
if (config("app.env") == "local") {
|
||||||
|
$utilPrefix = "util/api/wechatpay";
|
||||||
|
}
|
||||||
Route::middleware('api')
|
Route::middleware('api')
|
||||||
->prefix("api/wechatpay")
|
->prefix($utilPrefix)
|
||||||
->group(base_path('routes/pay/wechat.php'));
|
->group(base_path('routes/pay/wechat.php'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
use Illuminate\Auth\Events\Login;
|
use Exception;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use WeChatPay\Builder;
|
use WeChatPay\Builder;
|
||||||
use WeChatPay\BuilderChainable;
|
use WeChatPay\BuilderChainable;
|
||||||
|
use WeChatPay\Crypto\AesGcm;
|
||||||
use WeChatPay\Crypto\Rsa;
|
use WeChatPay\Crypto\Rsa;
|
||||||
|
|
||||||
class WechatPayService
|
class WechatPayService
|
||||||
@ -59,7 +60,7 @@ class WechatPayService
|
|||||||
* 商家转账
|
* 商家转账
|
||||||
* @return void
|
* @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))) {
|
if (empty(count($transfer_scene_report_infos))) {
|
||||||
@ -83,6 +84,7 @@ class WechatPayService
|
|||||||
"openid" => $openid,
|
"openid" => $openid,
|
||||||
"transfer_amount" => $amount,
|
"transfer_amount" => $amount,
|
||||||
"transfer_remark" => $remark,
|
"transfer_remark" => $remark,
|
||||||
|
"notify_url" => $notify_url,
|
||||||
"transfer_scene_report_infos" => $transfer_scene_report_infos
|
"transfer_scene_report_infos" => $transfer_scene_report_infos
|
||||||
];
|
];
|
||||||
Log::info("转账数据", $data);
|
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', [WechatPayController::class, "mchTransfer"])->middleware('merchant_user');
|
||||||
|
Route::post('saas/mch/transfer/callback', [WechatPayController::class, "mchTransferCallback"]);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user