{{wikiTitle}}
支付流程全量说明
目录:
支付流程全量说明
概述
CRMEB多店系统支持多种支付方式,包括微信支付、支付宝支付、余额支付、积分支付、线下支付等。本文档详细说明整个支付体系的架构、流程和实现细节。
支付方式类型
1.1 支付方式列表
系统支持的主要支付方式:
| 支付方式 | 标识 | 说明 |
|---|---|---|
| 微信支付 | weixin |
微信公众号、小程序支付 |
| 微信H5支付 | weixinh5 |
微信H5支付 |
| 支付宝支付 | alipay |
支付宝APP、H5、扫码支付 |
| 余额支付 | yue |
用户账户余额支付 |
| 积分支付 | integral |
用户积分抵扣支付 |
| 线下支付 | offline |
线下现金支付 |
| 现金支付 | cash |
现金支付 |
1.2 支付场景
- 商品购买支付 - 购买商品、服务
- 会员储值支付 - 购买会员卡
- 余额充值支付 - 用户账户充值
- 其他支付 - 各种自定义支付场景
支付系统架构
2.1 核心组件
支付系统架构:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 支付入口 │ │ 支付服务 │ │ 支付渠道 │
│ PayServices │◄──►│ OrderPayServices│◄──►│ 微信支付/支付宝 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 支付回调处理 │ │ 支付完成处理 │ │ 支付异步通知 │
│ PayNotifyServices│ │ StoreOrderSuccess│ │ 事件监听 │
│ │ │ Services │ │ PayNotifyListener│
└─────────────────┘ └─────────────────┘ └─────────────────┘
2.2 主要文件结构
app/services/pay/
├── PayServices.php # 支付统一入口
├── OrderPayServices.php # 订单支付服务
├── PayNotifyServices.php # 支付回调处理
├── PayMchNotifyServices.php # 商户支付回调
├── YuePayServices.php # 余额支付服务
├── IntegralPayServices.php # 积分支付服务
├── RechargeServices.php # 充值支付服务
└── OrderOfflineServices.php # 线下支付服务
app/listener/pay/
├── PayNotifyListener.php # 支付回调监听器
├── PayMchNotifyListener.php # 商户回调监听器
├── PayCreateOrderListener.php # 订单创建监听器
└── RefundedNotifyListener.php # 退款回调监听器
crmeb/services/
├── AliPayService.php # 支付宝服务
└── wechat/
└── Payment.php # 微信支付服务
app/controller/api/v1/Pay.php # 支付回调控制器
支付流程详解
3.1 发起支付流程
3.1.1 统一支付入口 (PayServices.php)
class PayServices
{
// 支付方式常量
const WEIXIN_PAY = 'weixin';
const YUE_PAY = 'yue';
const ALIAPY_PAY = 'alipay';
// ...
public function pay(string $payType, string $openid, string $orderId, string $price,
string $successAction, string $body, bool $isCode = false)
{
switch ($payType) {
case 'routine': // 微信小程序支付
if (request()->isApp()) {
return Payment::appPay($openid, $orderId, $price, $successAction, $body);
} else {
if (sys_config('pay_routine_open', 0)) {
return Payment::miniPay($openid, $orderId, $price, $successAction, $body);
} else {
// V3支付
if (Payment::instance()->isV3PAy) {
return Payment::instance()->application()->v3pay->miniprogPay($openid, $orderId, $price, $body, $successAction);
}
return Payment::jsPay($openid, $orderId, $price, $successAction, $body);
}
}
case 'weixinh5': // 微信H5支付
if (Payment::instance()->isV3PAy) {
return Payment::instance()->application()->v3pay->h5Pay($orderId, $price, $body, $successAction);
}
return Payment::paymentOrder(null, $orderId, $price, $successAction, $body, '', 'MWEB');
case self::WEIXIN_PAY: // 微信支付
if ($this->authCode) { // 付款码支付
return Payment::microPay($this->authCode, $orderId, $price, $successAction, $body);
} else {
// APP支付
if (request()->isApp()) {
return Payment::appPay($openid, $orderId, $price, $successAction, $body);
} else {
// V3支付
if (Payment::instance()->isV3PAy) {
return Payment::instance()->application()->v3pay->jsapiPay($openid, $orderId, $price, $body, $successAction);
}
// V2支付
return Payment::jsPay($openid, $orderId, $price, $successAction, $body);
}
}
case self::ALIAPY_PAY: // 支付宝支付
if ($this->authCode) { // 付款码支付
return AliPayService::instance()->microPay($this->authCode, $body, $orderId, $price, $successAction);
} else {
return AliPayService::instance()->create($body, $orderId, $price, $successAction, $openid, $openid, $isCode);
}
case 'pc': // PC端支付
case 'store': // 门店支付
return Payment::nativePay($openid, $orderId, $price, $successAction, $body);
default:
throw new ValidateException('支付方式不存在');
}
}
}
3.1.2 订单支付服务 (OrderPayServices.php)
class OrderPayServices
{
protected $payServices;
public function __construct(PayServices $services)
{
$this->payServices = $services;
}
/**
* 订单发起支付
*/
public function orderPay(array $orderInfo, string $payType)
{
// 1. 检查订单状态
if ($orderInfo['paid']) {
throw new ValidateException('订单已支付!');
}
if ($orderInfo['pay_price'] <= 0) {
throw new ValidateException('该支付无需支付!');
}
// 2. 获取用户openid(非特定支付方式需要)
$openid = '';
if (!in_array($payType, ['weixinh5', 'pc', 'store']) && !request()->isApp()) {
if ($payType === 'weixin') {
$userType = 'wechat';
} else {
$userType = $payType;
}
$services = app()->make(WechatUserServices::class);
$openid = $services->uidToOpenid($orderInfo['uid'], $userType);
if (!$openid) {
throw new ValidateException('获取用户openid失败,无法支付');
}
}
// 3. 生成支付描述
$site_name = sys_config('site_name');
$orderInfoServices = app()->make(StoreOrderCartInfoServices::class);
$body = $orderInfoServices->getCarIdByProductTitle((int)$orderInfo['id']);
$body = substrUTf8($site_name . '--' . $body, 30);
$successAction = "product";
if (!$body) {
throw new ValidateException('支付参数缺少:请前往后台设置->系统设置-> 填写 网站名称');
}
// 4. 发起支付
return $this->payServices->pay($payType, $openid, $orderInfo['order_id'],
$orderInfo['pay_price'], $successAction, $body);
}
}
3.2 微信支付流程
3.2.1 支付宝支付服务 (AliPayService.php)
class AliPayService
{
protected $config = [
'appId' => '',
'merchantPrivateKey' => '', // 应用私钥
'alipayPublicKey' => '', // 支付宝公钥
'notifyUrl' => '', // 异步通知地址
'encryptKey' => '', // AES密钥
];
/**
* 付款码支付
*/
public function microPay(string $authCode, string $title, string $orderId,
string $totalAmount, string $passbackParams)
{
try {
$result = Factory::payment()->faceToFace()->optional('passback_params', $passbackParams)
->pay($title, $orderId, $totalAmount, $authCode);
// 触发支付创建事件
$data = [
"subject" => $title,
"out_trade_no" => $orderId,
"total_amount" => $totalAmount,
"auth_code" => $authCode,
"scene" => "bar_code",
"passback_params" => $passbackParams
];
Event::until('pay.create.order', [$data, $orderId, 'alipay']);
if ($this->response->success($result)) {
$response = $result->toMap();
return [
'paid' => $response['code'] === '10000' ? 1 : 0,
'message' => $response['sub_msg'] ?? '支付成功',
'payInfo' => $response
];
} else {
throw new PayException('失败原因:' . $result->msg . ',' . $result->subMsg);
}
} catch (\Exception $e) {
throw new PayException($e->getMessage());
}
}
/**
* 创建支付订单
*/
public function create(string $title, string $orderId, string $totalAmount,
string $passbackParams, string $quitUrl = '', string $siteUrl = '', bool $isCode = false)
{
try {
if ($isCode) {
// 二维码支付
$result = Factory::payment()->faceToFace()->optional('passback_params', $passbackParams)
->precreate($title, $orderId, $totalAmount);
} else if (request()->isApp()) {
// APP支付
$result = Factory::payment()->app()->optional('passback_params', $passbackParams)
->pay($title, $orderId, $totalAmount);
} else {
// H5支付
$result = Factory::payment()->wap()->optional('passback_params', $passbackParams)
->pay($title, $orderId, $totalAmount, $quitUrl, $siteUrl);
}
Event::until('pay.create.order', [$data, $orderId, 'alipay']);
if ($this->response->success($result)) {
return isset($result->body) ? $result->body : $result;
} else {
throw new PayException('失败原因:' . $result->msg . ',' . $result->subMsg);
}
} catch (\Exception $e) {
throw new PayException($e->getMessage());
}
}
/**
* 处理异步回调
*/
public static function handleNotify()
{
return self::instance()->notify(function ($notify) {
if (isset($notify['out_trade_no'])) {
$res = Event::until('pay.notify', [$notify, 'aliyun']);
if ($res) {
return $res;
} else {
return false;
}
}
});
}
/**
* 异步回调处理
*/
public function notify(callable $notifyFn)
{
$paramInfo = app()->request->param();
unset($paramInfo['type']);
// 商户订单号
$postOrder['out_trade_no'] = $paramInfo['out_trade_no'] ?? '';
// 支付宝交易号
$postOrder['trade_no'] = $paramInfo['trade_no'] ?? '';
// 交易状态
$postOrder['trade_status'] = $paramInfo['trade_status'] ?? '';
// 备注
$postOrder['attach'] = isset($paramInfo['passback_params']) ? urldecode($paramInfo['passback_params']) : '';
if (in_array($postOrder['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED']) && $this->verifyNotify($paramInfo)) {
try {
if ($notifyFn($postOrder)) {
return 'success';
}
} catch (\Exception $e) {
Log::error('支付宝异步会回调成功,执行函数错误。错误单号:' . $postOrder['out_trade_no']);
}
}
return 'fail';
}
}
3.2.2 微信支付服务
微信支付由 crmeb\services\wechat\Payment 类处理,支持多种支付方式:
- JSAPI支付 (公众号/小程序)
- APP支付
- H5支付
- Native支付 (扫码支付)
- Micro支付 (付款码支付)
3.3 余额支付流程
3.3.1 余额支付服务 (YuePayServices.php)
class YuePayServices extends BaseServices
{
/**
* 订单余额支付
*/
public function yueOrderPay(array $orderInfo, int $uid)
{
if (!$orderInfo) {
throw new ValidateException('订单不存在');
}
if ($orderInfo['paid']) {
throw new ValidateException('该订单已支付!');
}
$type = 'pay_product';
if (isset($orderInfo['member_type'])) {
$type = 'pay_member';
}
// 获取用户信息
$services = app()->make(UserServices::class);
$userInfo = $services->getUserInfo($uid);
// 检查余额是否足够
if ($userInfo['now_money'] < $orderInfo['pay_price']) {
return ['status' => 'pay_deficiency', 'msg' => '余额不足' . floatval($orderInfo['pay_price'])];
}
// 执行支付
$this->transaction(function () use ($services, $orderInfo, $userInfo, $type) {
// 扣除用户余额
$res = false !== $services->bcDec($userInfo['uid'], 'now_money', $orderInfo['pay_price'], 'uid');
switch ($type) {
case 'pay_product': // 商品支付
$id = $orderInfo['id'] ?? 0;
$orderSerives = app()->make(StoreOrderServices::class);
$orderInfo = $orderSerives->get($id)->toArray();
// 记录余额变动
$now_money = bcsub((string)$userInfo['now_money'], (string)$orderInfo['pay_price'], 2);
$number = $orderInfo['pay_price'];
$userMoneyServices = app()->make(UserMoneyServices::class);
$res = $res && $userMoneyServices->income('pay_product', $userInfo['uid'], $number, $now_money, $orderInfo['id']);
// 订单支付成功处理
$orderServices = app()->make(StoreOrderSuccessServices::class);
$res = $res && $orderServices->paySuccess($orderInfo, PayServices::YUE_PAY, ['userInfo' => $userInfo]);
break;
case 'pay_member': // 会员支付
$OtherOrderServices = app()->make(OtherOrderServices::class);
$res = $res && $OtherOrderServices->paySuccess($orderInfo, PayServices::YUE_PAY, ['userInfo' => $userInfo]);
break;
}
if (!$res) {
throw new ValidateException('余额支付失败!');
}
});
return ['status' => true];
}
}
支付回调处理
4.1 异步回调流程
外部支付平台
│
▼
API回调接口 (/api/pay/notify/{type})
│
▼
支付类型判断 (微信/支付宝)
│
▼
支付验证 (验签)
│
▼
触发事件 (pay.notify)
│
▼
监听器 (PayNotifyListener)
│
▼
回调处理服务 (PayNotifyServices)
│
▼
订单状态更新
│
▼
返回处理结果
4.2 支付回调控制器 (Pay.php)
class Pay
{
/**
* 支付回调
*/
public function notify(string $type)
{
switch (urldecode($type)) {
case 'alipay':
return AliPayService::handleNotify(); // 支付宝回调
break;
case 'routine':
return Payment::instance()->setAccessEnd(Payment::MINI)->handleNotify(); // 小程序回调
break;
case 'wechat':
return Payment::instance()->setAccessEnd(Payment::WEB)->handleNotify(); // 公众号回调
break;
case 'app':
return Payment::instance()->setAccessEnd(Payment::APP)->handleNotify(); // APP回调
break;
}
}
/**
* 退款回调
*/
public function refund(string $type)
{
switch (urldecode($type)) {
case 'routine':
return Payment::instance()->setAccessEnd(Payment::MINI)->handleRefundedNotify();
break;
case 'wechat':
return Payment::instance()->setAccessEnd(Payment::WEB)->handleRefundedNotify();
break;
case 'app':
return Payment::instance()->setAccessEnd(Payment::APP)->handleRefundedNotify();
break;
}
}
}
4.3 支付回调监听器 (PayNotifyListener.php)
class PayNotifyListener
{
public function handle($event)
{
[$notify, $type] = $event;
if (isset($notify['attach']) && $notify['attach']) {
$outTradeNo = $notify['out_trade_no'];
if ($notify['attach'] == 'product') { // 购买商品
// 记录支付原始返回数据
$orderService = app()->make(StoreOrderSuccessServices::class);
$orderService->update(['order_id' => $outTradeNo], ['notify_data' => json_encode($notify)]);
}
// 处理订单号格式
if (($count = strpos($notify['out_trade_no'], '_')) !== false) {
if ($type == 'aliyun') {
$notify['trade_no'] = $notify->out_trade_no;
}
$notify['out_trade_no'] = substr($notify['out_trade_no'], $count + 1);
}
$tradeNo = $type == 'wechat' ? $notify['transaction_id'] : $notify['trade_no'];
// 调用对应的回调处理方法
return (new Hook(PayNotifyServices::class, $type))->listen($notify['attach'], $outTradeNo, $tradeNo);
}
return false;
}
}
4.4 支付回调处理服务 (PayNotifyServices.php)
class PayNotifyServices
{
/**
* 微信商品支付成功回调
*/
public function wechatProduct(string $order_id = null, string $trade_no = null)
{
try {
$services = app()->make(StoreOrderSuccessServices::class);
$orderInfo = $services->getOne(['order_id' => $order_id]);
if (!$orderInfo) {
$orderInfo = $services->getOne(['unique' => $order_id]);
if (!$orderInfo) return true;
}
if ($orderInfo->paid) return true; // 已支付
return $services->paySuccess($orderInfo->toArray(), PayServices::WEIXIN_PAY, ['trade_no' => $trade_no]);
} catch (\Exception $e) {
return false;
}
}
/**
* 微信充值成功回调
*/
public function wechatUserRecharge(string $order_id = null, string $trade_no = null)
{
try {
$userRecharge = app()->make(UserRechargeServices::class);
if ($userRecharge->be(['order_id' => $order_id, 'paid' => 1])) return true;
return $userRecharge->rechargeSuccess($order_id, ['trade_no' => $trade_no]);
} catch (\Exception $e) {
return false;
}
}
/**
* 支付宝商品支付回调
*/
public function aliyunProduct(string $order_id = null, string $trade_no = null)
{
if (!$order_id || !$trade_no) {
return false;
}
try {
$services = app()->make(StoreOrderSuccessServices::class);
$orderInfo = $services->getOne(['order_id' => $order_id]);
if (!$orderInfo) return true;
if ($orderInfo->paid) return true;
return $services->paySuccess($orderInfo->toArray(), PayServices::ALIAPY_PAY, ['trade_no' => $trade_no]);
} catch (\Throwable $e) {
return false;
}
}
}
支付完成处理
5.1 订单支付成功处理 (StoreOrderSuccessServices.php)
class StoreOrderSuccessServices
{
/**
* 订单支付成功
*/
public function paySuccess(array $orderInfo, string $payType, array $extend = [])
{
$this->transaction(function () use ($orderInfo, $payType, $extend) {
$updateData = [
'paid' => 1,
'pay_type' => $payType,
'pay_time' => date('Y-m-d H:i:s'),
'unique' => $orderInfo['order_id']
];
// 更新订单状态
$res = $this->dao->update(['id' => $orderInfo['id']], $updateData);
// 记录支付日志
$res = $res && $this->addPayLog($orderInfo['order_id'], $payType, $orderInfo['pay_price'], $extend);
// 触发支付成功事件
$res = $res && Event::until('order.pay.success', [$orderInfo, $payType, $extend]);
if (!$res) {
throw new ValidateException('订单支付成功处理失败');
}
});
return true;
}
}
支付配置
6.1 支付配置管理
支付相关的配置信息存储在系统配置表中:
- 微信支付配置:
wechat_appid,wechat_mch_id,wechat_key,wechat_cert_pem,wechat_key_pem - 支付宝配置:
ali_pay_appid,alipay_merchant_private_key,alipay_public_key - 支付开关:
pay_wechat_open,pay_alipay_open,pay_yue_open,pay_integral_open
6.2 支付配置获取
// 获取微信支付配置
$wechatConfig = SystemConfigService::more([
'wechat_appid', 'wechat_mch_id', 'wechat_key', 'wechat_cert_pem', 'wechat_key_pem'
]);
// 获取支付宝配置
$alipayConfig = SystemConfigService::more([
'ali_pay_appid', 'alipay_merchant_private_key', 'alipay_public_key'
]);
支付安全措施
7.1 验签机制
- 支付宝:使用官方SDK的验签功能
- 微信:使用微信官方SDK的验签功能
7.2 重复支付防护
- 通过订单状态检查防止重复支付
- 使用分布式锁防止并发支付
7.3 支付金额校验
- 支付前校验订单金额与支付金额一致性
- 防止篡改支付金额
前端支付集成
8.1 前端支付API
// 发起支付
export function payOrder(orderId, payType) {
return request.post('pay/order', {
order_id: orderId,
pay_type: payType
});
}
// 余额支付
export function yuePayOrder(orderId) {
return request.post('pay/yue', {
order_id: orderId
});
}
8.2 支付结果处理
前端需要处理不同的支付结果:
- 微信支付:调用微信JS SDK
- 支付宝支付:处理支付宝返回的支付信息
- 余额支付:直接跳转支付成功页面
常见问题处理
9.1 支付失败处理
- 检查支付配置是否正确
- 检查网络连接
- 检查订单状态
- 查看支付日志
9.2 异步回调失败
- 检查回调地址是否正确
- 检查服务器防火墙设置
- 检查SSL证书
- 查看服务器日志
9.3 订单状态不一致
- 检查异步回调是否正常处理
- 检查数据库事务是否正确
- 检查重复支付防护机制
扩展支付方式
10.1 添加新的支付方式
- 在
PayServices中添加支付方式常量 - 在
pay方法中添加对应处理逻辑 - 创建相应的支付服务类
- 添加支付回调处理方法
- 更新前端支付选择界面
10.2 自定义支付渠道
// 自定义支付服务示例
class CustomPayService
{
public function pay($orderId, $amount, $extra = [])
{
// 实现自定义支付逻辑
// 调用第三方支付接口
// 返回支付信息
}
public function handleNotify($data)
{
// 处理自定义支付回调
// 验证数据
// 更新订单状态
// 返回处理结果
}
}
事件钩子
系统提供了以下支付相关事件:
pay.create.order- 支付订单创建事件pay.notify- 支付回调事件order.pay.success- 订单支付成功事件pay.refund- 支付退款事件
这些事件可以通过监听器进行扩展处理。
评论({{cateWiki.comment_num}})
{{commentWhere.order ? '评论从旧到新':'评论从新到旧'}}
{{cateWiki.page_view_num}}人看过该文档
评论(0)
{{commentWhere.order ? '评论从旧到新':'评论从新到旧'}}
9人看过该文档
{{item.user ? item.user.nickname : ''}}
(自评)
{{item.content}}
{{item.create_time}}
删除
搜索结果
为您找到{{wikiCount}}条结果
{{item.page_view_num}}
{{item.like ? item.like.like_num : 0}}
{{item.comment ? item.comment.comment_num : 0}}
位置:
{{path.name}}
{{(i+1) == item.catalogue.path_data.length ? '':'/'}}