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
|
// ProcessPaymentWebhook handles the webhook callback from ArifPay
|
||||||
func (s *ArifpayService) ProcessPaymentWebhook(ctx context.Context, req domain.WebhookRequest) error {
|
func (s *ArifpayService) ProcessPaymentWebhook(ctx context.Context, req domain.WebhookRequest) error {
|
||||||
// Get payment by nonce
|
// ArifPay verify/webhook payloads are inconsistent: some responses include nonce, others only sessionId.
|
||||||
payment, err := s.paymentStore.GetPaymentByNonce(ctx, req.Nonce)
|
payment, err := s.resolvePaymentForWebhook(ctx, req)
|
||||||
if err != nil {
|
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) {
|
if payment.Status == string(domain.PaymentStatusSuccess) {
|
||||||
return ErrPaymentAlreadyPaid
|
return ErrPaymentAlreadyPaid
|
||||||
|
|
@ -216,12 +217,16 @@ func (s *ArifpayService) ProcessPaymentWebhook(ctx context.Context, req domain.W
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update payment status
|
// Update payment status
|
||||||
|
paymentMethod := req.PaymentMethod
|
||||||
|
if paymentMethod == "" && payment.PaymentMethod != nil {
|
||||||
|
paymentMethod = *payment.PaymentMethod
|
||||||
|
}
|
||||||
if err := s.paymentStore.UpdatePaymentStatusByNonce(
|
if err := s.paymentStore.UpdatePaymentStatusByNonce(
|
||||||
ctx,
|
ctx,
|
||||||
req.Nonce,
|
nonce,
|
||||||
newStatus,
|
newStatus,
|
||||||
req.Transaction.TransactionID,
|
req.Transaction.TransactionID,
|
||||||
req.PaymentMethod,
|
paymentMethod,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return fmt.Errorf("failed to update payment status: %w", err)
|
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)
|
expiresAt := domain.CalculateExpiryDate(startsAt, plan.DurationValue, plan.DurationUnit)
|
||||||
activeStatus := string(domain.SubscriptionStatusActive)
|
activeStatus := string(domain.SubscriptionStatusActive)
|
||||||
autoRenew := false
|
autoRenew := false
|
||||||
paymentRef := payment.Nonce
|
paymentRef := nonce
|
||||||
paymentMethod := req.PaymentMethod
|
|
||||||
|
|
||||||
subscription, err := s.subscriptionStore.CreateUserSubscription(ctx, domain.CreateUserSubscriptionInput{
|
subscription, err := s.subscriptionStore.CreateUserSubscription(ctx, domain.CreateUserSubscriptionInput{
|
||||||
UserID: payment.UserID,
|
UserID: payment.UserID,
|
||||||
|
|
@ -263,6 +267,23 @@ func (s *ArifpayService) ProcessPaymentWebhook(ctx context.Context, req domain.W
|
||||||
return nil
|
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
|
// VerifyPayment checks the status of a payment with ArifPay
|
||||||
func (s *ArifpayService) VerifyPayment(ctx context.Context, sessionID string) (*domain.Payment, error) {
|
func (s *ArifpayService) VerifyPayment(ctx context.Context, sessionID string) (*domain.Payment, error) {
|
||||||
// Get local payment record
|
// 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 {
|
if err := json.Unmarshal(respBytes, &result); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse ArifPay response: %w", err)
|
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
|
// Process the verification result same as webhook
|
||||||
if err := s.ProcessPaymentWebhook(ctx, result); err != nil && err != ErrPaymentAlreadyPaid {
|
if err := s.ProcessPaymentWebhook(ctx, result); err != nil && err != ErrPaymentAlreadyPaid {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user