webdna / commerce-braintree

Braintree gateway for Craft Commerce
Other
2 stars 10 forks source link

Card charged without success state on front-end #32

Closed zackspear closed 2 years ago

zackspear commented 3 years ago

Recently we received a support request from a customer claiming to be double charged. I was able to figure out how it happened to them.

If the user enters a postal code with invalid characters like 12345-____, they can proceed to payment and submit payment via the Drop-in UI (I realize I need to build in validation to prevent users from progressing to the next checkout step).

First attempt with invalid postal code results in this, which should be expected. Not sure why the error message is tripled. Screen Shot 2021-02-26 at 13 37 33 Here's the gateway response from the transaction details

{
    "errors": [
        [],
        [],
        []
    ],
    "params": {
        "transaction": {
            "type": "sale",
            "amount": "59",
            "orderId": "REDACTED_ORDER_ID",
            "options": {
                "submitForSettlement": "true"
            },
            "customer": {
                "firstName": "Zack",
                "lastName": "Spear",
                "email": "REDACTED"
            },
            "paymentMethodNonce": "tokencc_OI_WHY_IS_THIS_A_NONCE_LOL",
            "merchantAccountId": "REDACTED_ID_GOES_HERE",
            "billing": {
                "firstName": "zack",
                "lastName": "spear",
                "company": "Test",
                "streetAddress": "2345 Test St.",
                "extendedAddress": null,
                "locality": "Testville",
                "region": "OR",
                "postalCode": "97217-____",
                "countryCodeAlpha2": "US"
            },
            "shipping": {
                "firstName": "zack",
                "lastName": "spear",
                "company": "Test",
                "streetAddress": "2345 Test St.",
                "extendedAddress": null,
                "locality": "Testville",
                "region": "OR",
                "postalCode": "97217-____",
                "countryCodeAlpha2": "US"
            }
        }
    },
    "message": "Postal code can only contain letters, numbers, spaces, and hyphens.\nPostal code can only contain letters, numbers, spaces, and hyphens.\nPostal code can only contain letters, numbers, spaces, and hyphens.",
    "creditCardVerification": null,
    "transaction": null,
    "subscription": null,
    "merchantAccount": null,
    "verification": null
}

After going back to the user info form and correcting the postal code then coming back to the payment form, upon submission the form responds with this error: Screen Shot 2021-02-26 at 13 40 08

Now from the user's perspective the transaction failed. However, looking at the order details in the CMS & in Braintree, this attempt succeeds.

Screen Shot 2021-02-26 at 14 26 13

Gateway response for the success. Seems to be missing some information.

{
    "success": true,
    "transaction": {
        "id": "[REDACTED]",
        "status": "submitted_for_settlement",
        "type": "sale",
        "currencyIsoCode": "USD",
        "amount": "59.00",
        "merchantAccountId": "[REDACTED]",
        "subMerchantAccountId": null,
        "masterMerchantAccountId": null,
        "orderId": "[REDACTED]",
        "createdAt": {
            "date": "2021-02-26 21:40:02.000000",
            "timezone_type": 3,
            "timezone": "UTC"
        },
        "updatedAt": {
            "date": "2021-02-26 21:40:04.000000",
            "timezone_type": 3,
            "timezone": "UTC"
        },
        "customer": {
            "id": null,
            "firstName": "Zack",
            "lastName": "Spear",
            "company": null,
            "email": "[REDACTED]",
            "website": null,
            "phone": null,
            "fax": null
        },
        "billing": {
            "id": null,
            "firstName": "zack",
            "lastName": "spear",
            "company": "Test",
            "streetAddress": "2345 Test St.",
            "extendedAddress": null,
            "locality": "Testville",
            "region": "OR",
            "postalCode": "97217-1234",
            "countryName": "United States of America",
            "countryCodeAlpha2": "US",
            "countryCodeAlpha3": "USA",
            "countryCodeNumeric": "840"
        },
        "refundId": null,
        "refundIds": [],
        "refundedTransactionId": null,
        "partialSettlementTransactionIds": [],
        "authorizedTransactionId": null,
        "settlementBatchId": null,
        "shipping": {
            "id": null,
            "firstName": "zack",
            "lastName": "spear",
            "company": "Test",
            "streetAddress": "2345 Test St.",
            "extendedAddress": null,
            "locality": "Testville",
            "region": "OR",
            "postalCode": "97217-1234",
            "countryName": "United States of America",
            "countryCodeAlpha2": "US",
            "countryCodeAlpha3": "USA",
            "countryCodeNumeric": "840"
        },
        "customFields": null,
        "avsErrorResponseCode": null,
        "avsPostalCodeResponseCode": "M",
        "avsStreetAddressResponseCode": "N",
        "cvvResponseCode": "M",
        "gatewayRejectionReason": null,
        "processorAuthorizationCode": "[REDACTED]",
        "processorResponseCode": "1000",
        "processorResponseText": "Approved",
        "additionalProcessorResponse": null,
        "voiceReferralNumber": null,
        "purchaseOrderNumber": null,
        "taxAmount": null,
        "taxExempt": false,
        "scaExemptionRequested": null,
        "processedWithNetworkToken": false,
        "creditCard": {
            "token": null,
            "bin": "[REDACTED]",
            "last4": "[REDACTED]",
            "cardType": "Visa",
            "expirationMonth": "[REDACTED]",
            "expirationYear": "[REDACTED]",
            "customerLocation": "US",
            "cardholderName": "Zack Spear",
            "imageUrl": "https://assets.braintreegateway.com/payment_method_logo/visa.png?environment=production",
            "prepaid": "No",
            "healthcare": "No",
            "debit": "No",
            "durbinRegulated": "No",
            "commercial": "No",
            "payroll": "No",
            "issuingBank": "[REDACTED]",
            "countryOfIssuance": "USA",
            "productId": "A",
            "globalId": null,
            "accountType": null,
            "uniqueNumberIdentifier": null,
            "venmoSdk": false
        },
        "statusHistory": [
            [],
            []
        ],
        "planId": null,
        "subscriptionId": null,
        "subscription": {
            "billingPeriodEndDate": null,
            "billingPeriodStartDate": null
        },
        "addOns": [],
        "discounts": [],
        "descriptor": [],
        "recurring": false,
        "channel": null,
        "serviceFeeAmount": null,
        "escrowStatus": null,
        "disbursementDetails": [],
        "disputes": [],
        "authorizationAdjustments": [],
        "paymentInstrumentType": "credit_card",
        "processorSettlementResponseCode": null,
        "processorSettlementResponseText": null,
        "networkResponseCode": "00",
        "networkResponseText": "Successful approval/completion or V.I.P. PIN verification is successful",
        "riskData": {
            "id": null,
            "decision": "Not Evaluated",
            "fraudServiceProvider": "fraud_protection"
        },
        "threeDSecureInfo": null,
        "shipsFromPostalCode": null,
        "shippingAmount": null,
        "discountAmount": null,
        "networkTransactionId": "[REDACTED]",
        "processorResponseType": "approved",
        "authorizationExpiresAt": {
            "date": "2021-03-05 21:40:04.000000",
            "timezone_type": 3,
            "timezone": "UTC"
        },
        "retryIds": [],
        "retriedTransactionId": null,
        "refundGlobalIds": [],
        "partialSettlementTransactionGlobalIds": [],
        "refundedTransactionGlobalId": null,
        "authorizedTransactionGlobalId": null,
        "globalId": "[REDACTED]",
        "retryGlobalIds": [],
        "retriedTransactionGlobalId": null,
        "retrievalReferenceNumber": null,
        "installmentCount": null,
        "installments": [],
        "refundedInstallments": [],
        "responseEmvData": null,
        "acquirerReferenceNumber": null,
        "merchantIdentificationNumber": "[REDACTED]",
        "terminalIdentificationNumber": "[REDACTED]",
        "merchantName": "[REDACTED]",
        "merchantAddress": {
            "streetAddress": null,
            "locality": "ANAHEIM",
            "region": "CA",
            "postalCode": "[REDACTED]",
            "phone": "[REDACTED]"
        },
        "pinVerified": false,
        "creditCardDetails": [],
        "customerDetails": [],
        "billingDetails": [],
        "shippingDetails": [],
        "subscriptionDetails": [],
        "graphQLId": "[REDACTED]"
    }
}

This failure also results in our order email failing as the custom template relies on paymentInstrumentType to display the payment type of CC or PayPayl. Screen Shot 2021-02-26 at 14 57 19 Job data from failed email

{
    "orderId": 103621,
    "orderData": {
        "number": "[REDACTED]",
        "reference": "[REDACTED]",
        "couponCode": null,
        "isCompleted": true,
        "dateOrdered": {
            "date": "2/26/2021",
            "time": "1:40 PM"
        },
        "datePaid": {
            "date": "2/26/2021",
            "time": "1:40 PM"
        },
        "dateAuthorized": null,
        "currency": "USD",
        "gatewayId": 2,
        "lastIp": "[REDACTED]",
        "message": null,
        "returnUrl": "/order?num=[REDACTED]",
        "cancelUrl": "/checkout/payment",
        "orderStatusId": "1",
        "orderLanguage": "en-US",
        "orderSiteId": 1,
        "origin": "web",
        "billingAddressId": null,
        "shippingAddressId": "100058",
        "makePrimaryShippingAddress": null,
        "makePrimaryBillingAddress": null,
        "shippingSameAsBilling": null,
        "billingSameAsShipping": null,
        "estimatedBillingAddressId": null,
        "estimatedShippingAddressId": null,
        "estimatedBillingSameAsShipping": null,
        "shippingMethodHandle": "freeShipping",
        "shippingMethodName": "Free Shipping",
        "customerId": 3,
        "registerUserOnOrderComplete": null,
        "paymentSourceId": null,
        "storedTotalPrice": 59,
        "storedTotalPaid": 0,
        "storedItemTotal": 59,
        "storedItemSubtotal": 0,
        "storedTotalShippingCost": 0,
        "storedTotalDiscount": 0,
        "storedTotalTax": 0,
        "storedTotalTaxIncluded": 0,
        "id": 103621,
        "tempId": null,
        "draftId": null,
        "revisionId": null,
        "uid": "[REDACTED]",
        "siteSettingsId": 106048,
        "fieldLayoutId": 48,
        "contentId": 50367,
        "enabled": true,
        "archived": false,
        "siteId": 1,
        "title": null,
        "slug": null,
        "uri": null,
        "dateCreated": {
            "date": "12/5/2020",
            "time": "1:16 PM"
        },
        "dateUpdated": {
            "date": "2/26/2021",
            "time": "1:40 PM"
        },
        "dateDeleted": null,
        "trashed": false,
        "propagateAll": false,
        "newSiteIds": [],
        "resaving": false,
        "duplicateOf": null,
        "previewing": false,
        "hardDelete": false,
        "ref": null,
        "status": "enabled",
        "structureId": null,
        "url": null,
        "guid": "[REDACTED]",
        "guidType": "Trial",
        "deviceCountOrder": "1",
        "customerType": "Business",
        "confirmEmail": "[REDACTED]",
        "oemCustomerFirstName": null,
        "oemCustomerLastName": null,
        "oemCustomerEmail": "",
        "referral": null,
        "referralOther": null,
        "newsletterSignupOld": null,
        "newsletterSignup": false,
        "fromRegistrationWizard": "letsBoogie",
        "registrationWizardSite": "[REDACTED]",
        "adjustmentSubtotal": 0,
        "adjustmentsTotal": 0,
        "paymentCurrency": "USD",
        "email": "[REDACTED]",
        "isPaid": true,
        "itemSubtotal": 59,
        "itemTotal": 59,
        "lineItems": [
            {
                "id": 58689,
                "weight": 0,
                "length": 0,
                "height": 0,
                "width": 0,
                "qty": 1,
                "note": "",
                "privateNote": "",
                "purchasableId": "5479",
                "orderId": 103621,
                "lineItemStatusId": null,
                "taxCategoryId": 1,
                "shippingCategoryId": 1,
                "dateCreated": "2021-02-26T13:22:24-08:00",
                "dateUpdated": "2021-02-26T13:40:04-08:00",
                "adjustments": [],
                "description": "Basic",
                "options": {
                    "FlashGUID": "[REDACTED]",
                    "FlashGUIDParam": "[REDACTED]",
                    "deviceCountOrder": "1"
                },
                "optionsSignature": "[REDACTED]",
                "onSale": false,
                "price": 59,
                "saleAmount": 0,
                "salePrice": 59,
                "sku": "new-basic",
                "total": 59,
                "priceAsCurrency": "$59.00",
                "saleAmountAsCurrency": "$0.00",
                "salePriceAsCurrency": "$59.00",
                "subtotalAsCurrency": "$59.00",
                "totalAsCurrency": "$59.00",
                "discountAsCurrency": "$0.00",
                "shippingCostAsCurrency": "$0.00",
                "taxAsCurrency": "$0.00",
                "taxIncludedAsCurrency": "$0.00",
                "adjustmentsTotalAsCurrency": "$0.00",
                "subtotal": 59
            }
        ],
        "orderAdjustments": [],
        "outstandingBalance": 0,
        "paidStatus": "paid",
        "recalculationMode": "none",
        "shortNumber": "4e25621",
        "totalPaid": 59,
        "total": 59,
        "totalPrice": 59,
        "totalQty": 1,
        "totalSaleAmount": 0,
        "totalTaxablePrice": 59,
        "totalWeight": 0,
        "adjustmentSubtotalAsCurrency": "$0.00",
        "adjustmentsTotalAsCurrency": "$0.00",
        "itemSubtotalAsCurrency": "$59.00",
        "itemTotalAsCurrency": "$59.00",
        "outstandingBalanceAsCurrency": "$0.00",
        "totalPaidAsCurrency": "$59.00",
        "totalAsCurrency": "$59.00",
        "totalPriceAsCurrency": "$59.00",
        "totalSaleAmountAsCurrency": "$0.00",
        "totalTaxablePriceAsCurrency": "$59.00",
        "totalTaxAsCurrency": "$0.00",
        "totalTaxIncludedAsCurrency": "$0.00",
        "totalShippingCostAsCurrency": "$0.00",
        "totalDiscountAsCurrency": "$0.00",
        "storedTotalPriceAsCurrency": "$59.00",
        "storedTotalPaidAsCurrency": "$0.00",
        "storedItemTotalAsCurrency": "$59.00",
        "storedItemSubtotalAsCurrency": "$0.00",
        "storedTotalShippingCostAsCurrency": "$0.00",
        "storedTotalDiscountAsCurrency": "$0.00",
        "storedTotalTaxAsCurrency": "$0.00",
        "storedTotalTaxIncludedAsCurrency": "$0.00",
        "paidStatusHtml": "<span class=\"commerceStatusLabel\"><span class=\"status green\"></span> Paid</span>",
        "customerLinkHtml": "<span><a href=\"https://unraid.net/admin/commerce/customers/3\">[REDACTED]</a></span>",
        "orderStatusHtml": "<span class=\"commerceStatusLabel\"><span class=\"status green\"></span> New</span>",
        "totalTax": 0,
        "totalTaxIncluded": 0,
        "totalShippingCost": 0,
        "totalDiscount": 0
    },
    "commerceEmailId": "1",
    "orderHistoryId": 44294,
    "description": null
}

I plan to build in validation to prevent postal code issues in the first place. However, I wanted to bring attention to the fact that even with a correction to the postal code we're hitting this issue.

If you need more information please let me know.

Thanks in advance.

zackspear commented 3 years ago

We had this happen again but with slightly different circumstances.

User chose USA as the country but then didn't select a state. Upon first payment submission they received an error of

US state codes must be two characters to meet PayPal Seller Protection requirements.

They went back and corrected that. Then attempted payment again. Which resulted in the same error as detailed above in the issue description.

Impossible to access an attribute ("paymentInstrumentType") on a null variable.

I went ahead and added validation for both the postal codes & now state selection to help prevent this. But ultimately seems like something is going wrong when first payment attempt somehow fails.

mcjackson18 commented 3 years ago

Hi @zackspear, sorry for the delay in looking into this. I can see the successful transaction response above has the paymentInstrumentType attribute. How are you accessing the paymentInstrumentType in your email template?

In your email template have you tried add some defensive code to ensure the paymentInstrumentType attribute exists as this could be causing the error on the site.

mcjackson18 commented 2 years ago

Closing this due to age.