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 } }