hyapp-server/services/wallet-service/internal/service/wallet/external_recharge_reconcile.go
2026-06-25 11:21:16 +08:00

131 lines
4.4 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
}
ctx = appcode.WithContext(ctx, appcode.Normalize(appCode))
orders, err := s.repository.ListExternalRechargeOrdersForReconcile(ctx, appcode.FromContext(ctx), limit)
if err != nil {
return 0, err
}
processed := 0
for _, order := range orders {
if _, err := s.refreshMifaPayRechargeOrder(ctx, order); err != nil {
processed++
continue
}
if _, err := s.refreshV5PayRechargeOrder(ctx, 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
}
}