104 lines
3.5 KiB
PHP
104 lines
3.5 KiB
PHP
<?php
|
|
namespace app\api\controller;
|
|
|
|
use support\Request;
|
|
use app\common\model\Order;
|
|
use app\common\model\PaymentTransaction;
|
|
use app\common\model\WechatMerchant;
|
|
use app\common\service\OrderFlowService;
|
|
use app\common\service\WechatPayV3Client;
|
|
use Illuminate\Database\Capsule\Manager as DB;
|
|
|
|
class PayController
|
|
{
|
|
public function wechatNotify(Request $request)
|
|
{
|
|
$body = (string)$request->rawBody();
|
|
|
|
$timestamp = (string)$request->header('Wechatpay-Timestamp', '');
|
|
$nonce = (string)$request->header('Wechatpay-Nonce', '');
|
|
$signature = (string)$request->header('Wechatpay-Signature', '');
|
|
if ($timestamp === '' || $nonce === '' || $signature === '') {
|
|
return json(['code' => 'FAIL', 'message' => 'missing headers'], 400);
|
|
}
|
|
|
|
$merchants = WechatMerchant::where('status', 1)->get();
|
|
if ($merchants->count() === 0) {
|
|
return json(['code' => 'FAIL', 'message' => 'no merchant'], 500);
|
|
}
|
|
|
|
$client = new WechatPayV3Client($merchants->first());
|
|
$ok = $client->verifyPlatformSignature($timestamp, $nonce, $body, $signature);
|
|
if (!$ok) {
|
|
return json(['code' => 'FAIL', 'message' => 'invalid signature'], 400);
|
|
}
|
|
|
|
$payload = json_decode($body, true) ?: [];
|
|
$resource = $payload['resource'] ?? null;
|
|
if (!is_array($resource)) {
|
|
return json(['code' => 'FAIL', 'message' => 'invalid body'], 400);
|
|
}
|
|
|
|
$decrypt = null;
|
|
$matchedMerchant = null;
|
|
foreach ($merchants as $m) {
|
|
$apiV3Key = (string)($m->api_v3_key ?? '');
|
|
if ($apiV3Key === '') continue;
|
|
try {
|
|
$decrypt = $client->decryptNotifyResource($resource, $apiV3Key);
|
|
$matchedMerchant = $m;
|
|
break;
|
|
} catch (\Throwable $e) {
|
|
}
|
|
}
|
|
if (!$decrypt || !$matchedMerchant) {
|
|
return json(['code' => 'FAIL', 'message' => 'decrypt failed'], 400);
|
|
}
|
|
|
|
$outTradeNo = (string)($decrypt['out_trade_no'] ?? '');
|
|
$tradeState = (string)($decrypt['trade_state'] ?? '');
|
|
if ($outTradeNo === '') {
|
|
return json(['code' => 'FAIL', 'message' => 'missing out_trade_no'], 400);
|
|
}
|
|
|
|
$tx = PaymentTransaction::where('out_trade_no', $outTradeNo)->first();
|
|
if (!$tx) {
|
|
return json(['code' => 'SUCCESS', 'message' => 'OK']);
|
|
}
|
|
|
|
if ($tradeState !== 'SUCCESS') {
|
|
return json(['code' => 'SUCCESS', 'message' => 'OK']);
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
$tx->status = 'paid';
|
|
$tx->paid_at = date('Y-m-d H:i:s');
|
|
$tx->raw_json = $decrypt;
|
|
$tx->save();
|
|
|
|
$order = Order::find($tx->order_id);
|
|
if ($order) {
|
|
$order->pay_channel = 'wechat';
|
|
$order->pay_status = 'paid';
|
|
$order->pay_merchant_id = (int)$matchedMerchant->id;
|
|
$order->pay_out_trade_no = $outTradeNo;
|
|
if (!$order->pay_time) {
|
|
$order->pay_time = date('Y-m-d H:i:s');
|
|
}
|
|
$order->save();
|
|
|
|
OrderFlowService::payOrder($order);
|
|
}
|
|
|
|
DB::commit();
|
|
} catch (\Throwable $e) {
|
|
DB::rollBack();
|
|
return json(['code' => 'FAIL', 'message' => 'server error'], 500);
|
|
}
|
|
|
|
return json(['code' => 'SUCCESS', 'message' => 'OK']);
|
|
}
|
|
}
|
|
|