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:
parent
408cd3fd7d
commit
d225b45166
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user