批零微信支付
parent
ce2a438f69
commit
7b7f998645
|
|
@ -212,43 +212,50 @@ class OrderController extends Controller
|
|||
*/
|
||||
public function payOrder($id, Request $request, OrderService $orderService)
|
||||
{
|
||||
$order = DealerOrder::findOrFail($id);
|
||||
$userId = $request->user()->id;
|
||||
//不是采购人
|
||||
if (!$order->isUser($userId)) {
|
||||
throw new BizException('订单未找到');
|
||||
}
|
||||
|
||||
$input = $request->validate([
|
||||
'pay_image' => ['bail', 'string'],
|
||||
'pay_way' => ['bail', 'string'],
|
||||
'pay_password' => ['bail', 'string'],
|
||||
], [], [
|
||||
'pay_password' => ['bail', 'required_if:pay_way,wallet'],
|
||||
], [
|
||||
'pay_password.required_if' => '支付密码 不能为空。',
|
||||
], [
|
||||
'pay_image' => '打款凭证',
|
||||
'pay_way' => '支付方式',
|
||||
'pay_password' => '支付密码',
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
$order = $user->dealerOrders()->findOrFail($id);
|
||||
|
||||
$payWay = $input['pay_way'] ?? 'offline';
|
||||
if ($payWay == DealerOrder::PAY_WAY_WALLET) {
|
||||
//验证支付密码
|
||||
if (! $request->user()->wallet?->verifyPassword($input['pay_password'] ?? '')) {
|
||||
|
||||
if (
|
||||
$payWay === DealerOrder::PAY_WAY_WALLET &&
|
||||
!$user->wallet?->verifyPassword($input['pay_password'])
|
||||
) {
|
||||
throw new PayPasswordIncorrectException();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$orderService->payOrder($order, $input['pay_way'] ?? 'offline', $input['pay_image'] ?? null);
|
||||
|
||||
$data = $orderService->pay($order, $payWay, $input['pay_image'] ?? null);
|
||||
|
||||
DB::commit();
|
||||
} catch (BizException $th) {
|
||||
} catch (Throwable $e) {
|
||||
DB::rollBack();
|
||||
throw $th;
|
||||
} catch (Throwable $th) {
|
||||
DB::rollBack();
|
||||
report($th);
|
||||
throw new BizException('操作失败,请刷新后再试');
|
||||
|
||||
report($e);
|
||||
|
||||
if (! $e instanceof BizException) {
|
||||
$e = new BizException('操作失败,请刷新后再试');
|
||||
}
|
||||
|
||||
return response()->noContent();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Endpoint\Callback\Http\Controllers;
|
||||
|
||||
use App\Exceptions\BizException;
|
||||
use App\Services\PayService;
|
||||
use App\Services\WeChatPayService;
|
||||
use EasyWeChat\Factory;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Throwable;
|
||||
|
||||
class YzkWeChatPayController extends Controller
|
||||
{
|
||||
/**
|
||||
* 支付结果通知
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function paidNotify()
|
||||
{
|
||||
$weChatPayService = new WeChatPayService(Factory::payment(config('wechat.payment.yzk')));
|
||||
|
||||
return $weChatPayService->handlePaidNotify(function ($message, $fail) {
|
||||
$this->log('paid notify', $message);
|
||||
|
||||
// 通信失败
|
||||
if (data_get($message, 'return_code') !== 'SUCCESS') {
|
||||
return $fail('通信失败');
|
||||
}
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($message) {
|
||||
$payService = new PayService();
|
||||
|
||||
if (data_get($message, 'result_code') === 'SUCCESS') {
|
||||
return $payService->handleSuccessByPaySerialNumber($message['out_trade_no'], [
|
||||
'out_trade_no' => $message['transaction_id'],
|
||||
'pay_at' => Carbon::parse($message['time_end']),
|
||||
]);
|
||||
} elseif (data_get($message, 'result_code') === 'FAIL') {
|
||||
return $payService->handleFailedByPaySerialNumber($message['out_trade_no'], [
|
||||
'out_trade_no' => $message['transaction_id'] ?? null,
|
||||
'failed_reason' => '['.$message['err_code'].']'.$message['err_code_des'],
|
||||
]);
|
||||
}
|
||||
});
|
||||
} catch (ModelNotFoundException | BizException $e) {
|
||||
} catch (Throwable $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信回调日志
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @return void
|
||||
*/
|
||||
protected function log(string $message, array $context = [])
|
||||
{
|
||||
return Log::build([
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/yzk-wxpay-notify.log'),
|
||||
])->info($message, $context);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
use App\Endpoint\Callback\Http\Controllers\AlipayController;
|
||||
use App\Endpoint\Callback\Http\Controllers\Kuaidi100Controller;
|
||||
use App\Endpoint\Callback\Http\Controllers\WeChatPayController;
|
||||
use App\Endpoint\Callback\Http\Controllers\YzkWeChatPayController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
//快递100物流推送
|
||||
|
|
@ -12,3 +13,5 @@ Route::post('wxpay/paid-notify', [WeChatPayController::class, 'paidNotify'])->na
|
|||
Route::post('wxpay/order-refund-notify', [WeChatPayController::class, 'orderRefundedNotify'])->name('wxpay.order_refund_notify');
|
||||
|
||||
Route::post('alipay', AlipayController::class)->name('alipay.notify');
|
||||
|
||||
Route::post('yzk-wxpay/paid-notify', [YzkWeChatPayController::class, 'paidNotify'])->name('yzk_wxpay.paid_notify');
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class DealerOrder extends Model
|
|||
|
||||
public const PAY_WAY_WALLET = 'wallet'; // 余额
|
||||
public const PAY_WAY_OFFLINE = 'offline'; // 线下支付
|
||||
public const PAY_WAY_WXPAY = 'wxpay'; // 线下支付
|
||||
|
||||
protected $attributes = [
|
||||
'status' => DealerOrderStatus::Pending,
|
||||
|
|
@ -50,6 +51,8 @@ class DealerOrder extends Model
|
|||
'shippinged_time',
|
||||
'allocated_at', //分配时间
|
||||
'remark',
|
||||
'pay_sn',
|
||||
'out_trade_no',
|
||||
];
|
||||
|
||||
public static $payWayText = [
|
||||
|
|
@ -290,4 +293,12 @@ class DealerOrder extends Model
|
|||
}
|
||||
return $payInfo ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 属于此订单的支付记录
|
||||
*/
|
||||
public function payLogs()
|
||||
{
|
||||
return $this->morphMany(PayLog::class, 'payable');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,12 @@ use App\Models\DealerOrderAllocateLog;
|
|||
use App\Models\DealerOrderProduct;
|
||||
use App\Models\DealerProduct;
|
||||
use App\Models\DealerUserProductLog;
|
||||
use App\Models\PayLog;
|
||||
use App\Models\ShippingAddress;
|
||||
use App\Models\User;
|
||||
use App\Models\UserInfo;
|
||||
use App\Services\PayService;
|
||||
use App\Services\WeChatPayService;
|
||||
use EasyWeChat\Factory as EasyWeChatFactory;
|
||||
use Illuminate\Database\QueryException;
|
||||
|
||||
class OrderService
|
||||
|
|
@ -264,6 +267,107 @@ class OrderService
|
|||
return $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付订单
|
||||
*
|
||||
* @param DealerOrder $order
|
||||
* @param string $payWay
|
||||
* @param string|null $payImage
|
||||
* @return array
|
||||
*
|
||||
* @throws \App\Exceptions\BizException
|
||||
*/
|
||||
public function pay(DealerOrder $order, string $payWay, ?string $payImage = null): array
|
||||
{
|
||||
if (! $order->isPendinged()) {
|
||||
throw new BizException('订单状态不是待付款');
|
||||
}
|
||||
|
||||
do {
|
||||
$payLog = null;
|
||||
|
||||
try {
|
||||
$payLog = $order->payLogs()->create([
|
||||
'pay_sn' => serial_number(),
|
||||
'pay_way' => $payWay,
|
||||
]);
|
||||
} catch (QueryException $e) {
|
||||
if (strpos($e->getMessage(), 'Duplicate entry') === false) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
} while ($payLog === null);
|
||||
|
||||
$data = [
|
||||
'pay_sn' => $payLog->pay_sn,
|
||||
];
|
||||
|
||||
switch ($payLog->pay_way) {
|
||||
case PayLog::PAY_WAY_OFFLINE:
|
||||
(new PayService())->handleSuccess($payLog, [
|
||||
'pay_image' => $payImage,
|
||||
'pay_info' => $order->getConsignorPayInfo() ?? null,
|
||||
'pay_at' => now(),
|
||||
]);
|
||||
|
||||
$data = null;
|
||||
|
||||
break;
|
||||
|
||||
case PayLog::PAY_WAY_WALLET:
|
||||
$walletService = new WalletService();
|
||||
|
||||
// 扣除打款人余额
|
||||
$walletService->changeBalance(
|
||||
$order->user,
|
||||
bcmul($order->total_amount, '-1', 2),
|
||||
DealerWalletAction::OrderPaid,
|
||||
"订单:{$order->sn}",
|
||||
$order
|
||||
);
|
||||
|
||||
// 增加收款人余额
|
||||
if ($order->consignor) {
|
||||
$walletService->changeBalance(
|
||||
$order->consignor,
|
||||
$order->total_amount,
|
||||
DealerWalletAction::OrderIncome,
|
||||
"订单:{$order->sn}",
|
||||
$order
|
||||
);
|
||||
}
|
||||
|
||||
(new PayService())->handleSuccess($payLog, [
|
||||
'pay_at' => now(),
|
||||
]);
|
||||
|
||||
$data = null;
|
||||
|
||||
break;
|
||||
|
||||
case PayLog::PAY_WAY_WXPAY_H5:
|
||||
$app = EasyWeChatFactory::payment(config('wechat.payment.yzk'));
|
||||
|
||||
$data = (new WeChatPayService($app))->pay([
|
||||
'body' => app_settings('app.app_name').'-批零订单',
|
||||
'out_trade_no' => $payLog->pay_sn,
|
||||
'total_fee' => bcmul($order->total_amount, '100'),
|
||||
'trade_type' => WeChatPayService::$tradeTypes[$payLog->pay_way],
|
||||
'notify_url' => url(route('yzk_wxpay.paid_notify', [], false), [], true),
|
||||
]);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new BizException('支付方式不支持');
|
||||
break;
|
||||
}
|
||||
|
||||
return [
|
||||
'pay_way' => $payLog->pay_way,
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认收款
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\DealerOrderStatus;
|
||||
use App\Exceptions\BizException;
|
||||
use App\Exceptions\InvalidPaySerialNumberException;
|
||||
use App\Models\DealerOrder;
|
||||
use App\Models\DistributionPreIncomeJob;
|
||||
use App\Models\Order;
|
||||
use App\Models\PayLog;
|
||||
|
|
@ -97,6 +99,32 @@ class PayService
|
|||
'jobable_type' => $payable->getMorphClass(),
|
||||
'remarks' => '支付订单',
|
||||
]);
|
||||
} elseif ($payable instanceof DealerOrder) {
|
||||
if (! $payable->isPendinged()) {
|
||||
throw new BizException('订单不是待打款');
|
||||
}
|
||||
|
||||
$payable->pay_sn = $payLog->pay_sn;
|
||||
$payable->pay_time = $payLog->pay_at;
|
||||
$payable->out_trade_no = $payLog->out_trade_no;
|
||||
|
||||
if ($payLog->isOffline()) {
|
||||
$payable->pay_image = $params['pay_image'] ?? null;
|
||||
$payable->pay_info = $params['pay_info'] ?? null;
|
||||
$payable->pay_way = DealerOrder::PAY_WAY_OFFLINE;
|
||||
$payable->status = DealerOrderStatus::Confirming;
|
||||
} else {
|
||||
if ($payLog->isWallet()) {
|
||||
$payable->pay_way = DealerOrder::PAY_WAY_WALLET;
|
||||
} elseif ($payLog->isWxpay()) {
|
||||
$payable->pay_way = DealerOrder::PAY_WAY_WXPAY;
|
||||
}
|
||||
|
||||
$payable->paied_time = $payLog->pay_at;
|
||||
$payable->status = DealerOrderStatus::Paid;
|
||||
}
|
||||
|
||||
$payable->save();
|
||||
}
|
||||
|
||||
return $payLog;
|
||||
|
|
|
|||
|
|
@ -23,5 +23,26 @@ return [
|
|||
],
|
||||
],
|
||||
],
|
||||
'yzk' => [
|
||||
'sandbox' => env('WECHAT_PAYMENT_SANDBOX_YZK', false),
|
||||
'app_id' => env('WECHAT_PAYMENT_APPID_YZK'),
|
||||
'mch_id' => env('WECHAT_PAYMENT_MCH_ID_YZK'),
|
||||
'key' => env('WECHAT_PAYMENT_KEY_YZK'),
|
||||
'cert_path' => env('WECHAT_PAYMENT_CERT_PATH_YZK'), // 绝对地址
|
||||
'key_path' => env('WECHAT_PAYMENT_KEY_PATH_YZK'), // 绝对地址
|
||||
'notify_url' => env('WECHAT_PAYMENT_NOTIFY_URL_YZK'),
|
||||
|
||||
// 日志
|
||||
'log' => [
|
||||
'default' => 'daily',
|
||||
'channels' => [
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/yzk-wxpay.log'),
|
||||
'level' => 'info',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddPaySnToDealerOrdersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('dealer_orders', function (Blueprint $table) {
|
||||
$table->string('pay_sn')->nullable()->comment('支付流水号');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('dealer_orders', function (Blueprint $table) {
|
||||
$table->dropColumn(['pay_sn']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddOutTradeNoToDealerOrdersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('dealer_orders', function (Blueprint $table) {
|
||||
$table->string('out_trade_no')->nullable()->comment('外部交易单号');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('dealer_orders', function (Blueprint $table) {
|
||||
$table->dropColumn(['out_trade_no']);
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue