Fix ArifPay verification when nonce is missing.

Resolve payments by nonce or session_id in webhook/verify processing so status checks can complete and activate subscriptions even when ArifPay verify responses omit nonce.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Yared Yemane 2026-05-28 02:01:47 -07:00
parent 408cd3fd7d
commit d225b45166

View File

@ -186,11 +186,12 @@ func (s *ArifpayService) InitiateSubscriptionPayment(ctx context.Context, userID
// ProcessPaymentWebhook handles the webhook callback from ArifPay
func (s *ArifpayService) ProcessPaymentWebhook(ctx context.Context, req domain.WebhookRequest) error {
// Get payment by nonce
payment, err := s.paymentStore.GetPaymentByNonce(ctx, req.Nonce)
// ArifPay verify/webhook payloads are inconsistent: some responses include nonce, others only sessionId.
payment, err := s.resolvePaymentForWebhook(ctx, req)
if err != nil {
return fmt.Errorf("payment not found for nonce %s: %w", req.Nonce, err)
return err
}
nonce := payment.Nonce
if payment.Status == string(domain.PaymentStatusSuccess) {
return ErrPaymentAlreadyPaid
@ -216,12 +217,16 @@ func (s *ArifpayService) ProcessPaymentWebhook(ctx context.Context, req domain.W
}
// Update payment status
paymentMethod := req.PaymentMethod
if paymentMethod == "" && payment.PaymentMethod != nil {
paymentMethod = *payment.PaymentMethod
}
if err := s.paymentStore.UpdatePaymentStatusByNonce(
ctx,
req.Nonce,
nonce,
newStatus,
req.Transaction.TransactionID,
req.PaymentMethod,
paymentMethod,
); err != nil {
return fmt.Errorf("failed to update payment status: %w", err)
}
@ -237,8 +242,7 @@ func (s *ArifpayService) ProcessPaymentWebhook(ctx context.Context, req domain.W
expiresAt := domain.CalculateExpiryDate(startsAt, plan.DurationValue, plan.DurationUnit)
activeStatus := string(domain.SubscriptionStatusActive)
autoRenew := false
paymentRef := payment.Nonce
paymentMethod := req.PaymentMethod
paymentRef := nonce
subscription, err := s.subscriptionStore.CreateUserSubscription(ctx, domain.CreateUserSubscriptionInput{
UserID: payment.UserID,
@ -263,6 +267,23 @@ func (s *ArifpayService) ProcessPaymentWebhook(ctx context.Context, req domain.W
return nil
}
func (s *ArifpayService) resolvePaymentForWebhook(ctx context.Context, req domain.WebhookRequest) (*domain.Payment, error) {
if req.Nonce != "" {
payment, err := s.paymentStore.GetPaymentByNonce(ctx, req.Nonce)
if err == nil {
return payment, nil
}
}
if req.SessionID != "" {
payment, err := s.paymentStore.GetPaymentBySessionID(ctx, req.SessionID)
if err == nil {
return payment, nil
}
return nil, fmt.Errorf("payment not found for session %s: %w", req.SessionID, err)
}
return nil, fmt.Errorf("payment not found for nonce %s: %w", req.Nonce, ErrPaymentNotFound)
}
// VerifyPayment checks the status of a payment with ArifPay
func (s *ArifpayService) VerifyPayment(ctx context.Context, sessionID string) (*domain.Payment, error) {
// Get local payment record
@ -304,6 +325,10 @@ func (s *ArifpayService) VerifyPayment(ctx context.Context, sessionID string) (*
if err := json.Unmarshal(respBytes, &result); err != nil {
return nil, fmt.Errorf("failed to parse ArifPay response: %w", err)
}
// Ensure fallback lookup key when ArifPay omits nonce in verify response.
if result.SessionID == "" {
result.SessionID = sessionID
}
// Process the verification result same as webhook
if err := s.ProcessPaymentWebhook(ctx, result); err != nil && err != ErrPaymentAlreadyPaid {