132 lines
4.5 KiB
Go
132 lines
4.5 KiB
Go
package wallet
|
||
|
||
import (
|
||
"context"
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/xerr"
|
||
"hyapp/services/wallet-service/internal/domain/ledger"
|
||
"strings"
|
||
)
|
||
|
||
func (s *Service) ReconcileExternalRechargeOrders(ctx context.Context, appCode string, limit int) (int, error) {
|
||
if s.repository == nil {
|
||
return 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
|
||
}
|
||
if limit <= 0 {
|
||
return 0, nil
|
||
}
|
||
appCode = strings.ToLower(strings.TrimSpace(appCode))
|
||
orders, err := s.repository.ListExternalRechargeOrdersForReconcile(ctx, appCode, limit)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
processed := 0
|
||
for _, order := range orders {
|
||
// 跨租户扫描后必须恢复订单自己的 context,后续幂等流水和账户更新不能落到默认 lalu。
|
||
orderCtx := appcode.WithContext(ctx, order.AppCode)
|
||
if _, err := s.refreshMifaPayRechargeOrder(orderCtx, order); err != nil {
|
||
processed++
|
||
continue
|
||
}
|
||
if _, err := s.refreshV5PayRechargeOrder(orderCtx, order); err != nil {
|
||
processed++
|
||
continue
|
||
}
|
||
processed++
|
||
}
|
||
return processed, nil
|
||
}
|
||
|
||
func (s *Service) refreshMifaPayRechargeOrder(ctx context.Context, order ledger.ExternalRechargeOrder) (ledger.ExternalRechargeOrder, error) {
|
||
if s.mifaPay == nil || order.ProviderCode != ledger.PaymentProviderMifaPay || isExternalRechargeTerminalStatus(order.Status) {
|
||
return order, nil
|
||
}
|
||
query, err := s.mifaPay.QueryOrder(ctx, MifaPayQueryOrderRequest{
|
||
OrderID: order.OrderID,
|
||
ProviderOrderID: order.ProviderOrderID,
|
||
OrderCreatedAtMS: order.CreatedAtMS,
|
||
})
|
||
if err != nil {
|
||
|
||
return order, err
|
||
}
|
||
providerOrderID := firstNonEmpty(query.ProviderOrderID, order.ProviderOrderID)
|
||
data := MifaPayNotifyData{
|
||
OrderID: firstNonEmpty(query.OrderID, order.OrderID),
|
||
ProviderOrderID: providerOrderID,
|
||
Amount: query.Amount,
|
||
Currency: query.Currency,
|
||
PayType: query.PayType,
|
||
RawJSON: query.RawJSON,
|
||
}
|
||
switch strings.TrimSpace(query.Status) {
|
||
case "1":
|
||
if !mifaPayQueryMatchesOrder(data, order) {
|
||
|
||
return s.repository.MarkExternalRechargeOrderFailed(ctx, order.AppCode, order.OrderID, "mifapay query order data mismatch", query.RawJSON)
|
||
}
|
||
if isTemporaryExternalRechargeOrder(order) {
|
||
return s.repository.MarkExternalRechargeOrderPaid(ctx, order.AppCode, order.OrderID, providerOrderID, query.RawJSON)
|
||
}
|
||
return s.repository.CreditExternalRechargeOrder(ctx, order.AppCode, order.OrderID, providerOrderID, "", query.RawJSON)
|
||
case "4":
|
||
return s.repository.MarkExternalRechargeOrderFailed(ctx, order.AppCode, order.OrderID, "mifapay order status 4", query.RawJSON)
|
||
case "0", "5", "":
|
||
return order, nil
|
||
default:
|
||
return order, nil
|
||
}
|
||
}
|
||
|
||
func (s *Service) refreshV5PayRechargeOrder(ctx context.Context, order ledger.ExternalRechargeOrder) (ledger.ExternalRechargeOrder, error) {
|
||
if s.v5Pay == nil || order.ProviderCode != ledger.PaymentProviderV5Pay || isExternalRechargeTerminalStatus(order.Status) {
|
||
return order, nil
|
||
}
|
||
query, err := s.v5Pay.QueryOrder(ctx, V5PayQueryOrderRequest{
|
||
OrderID: order.OrderID,
|
||
ProviderOrderID: order.ProviderOrderID,
|
||
CountryCode: order.CountryCode,
|
||
CurrencyCode: order.CurrencyCode,
|
||
})
|
||
if err != nil {
|
||
|
||
return order, err
|
||
}
|
||
providerOrderID := firstNonEmpty(query.ProviderOrderID, order.ProviderOrderID)
|
||
data := V5PayNotifyData{
|
||
OrderID: firstNonEmpty(query.OrderID, order.OrderID),
|
||
ProviderOrderID: providerOrderID,
|
||
OrderStatus: query.Status,
|
||
Amount: query.Amount,
|
||
Currency: query.Currency,
|
||
ProductType: query.ProductType,
|
||
RawJSON: query.RawJSON,
|
||
}
|
||
switch strings.TrimSpace(query.Status) {
|
||
case "2":
|
||
if !v5PayOrderDataMatchesOrder(data, order) {
|
||
|
||
return s.repository.MarkExternalRechargeOrderFailed(ctx, order.AppCode, order.OrderID, "v5pay query order data mismatch", query.RawJSON)
|
||
}
|
||
if isTemporaryExternalRechargeOrder(order) {
|
||
return s.repository.MarkExternalRechargeOrderPaid(ctx, order.AppCode, order.OrderID, providerOrderID, query.RawJSON)
|
||
}
|
||
return s.repository.CreditExternalRechargeOrder(ctx, order.AppCode, order.OrderID, providerOrderID, "", query.RawJSON)
|
||
case "3", "5", "6":
|
||
return s.repository.MarkExternalRechargeOrderFailed(ctx, order.AppCode, order.OrderID, "v5pay order status "+query.Status, query.RawJSON)
|
||
case "0", "1", "":
|
||
return order, nil
|
||
default:
|
||
return order, nil
|
||
}
|
||
}
|
||
|
||
func isExternalRechargeTerminalStatus(status string) bool {
|
||
switch strings.TrimSpace(status) {
|
||
case ledger.ExternalRechargeStatusCredited, ledger.ExternalRechargeStatusPaid, ledger.ExternalRechargeStatusFailed:
|
||
return true
|
||
default:
|
||
return false
|
||
}
|
||
}
|