'Sending billing address with react-paypal-js hosted fields

Has anyone ever sent a billing address using the submit() functionality of the react-paypal-js hosted fields? I'm doing this:

    hostedFields
        .submit({
            // The full name as shown in the card and billing address
            cardHolderName: cardHolderName,
            billingAddress: billingAddress
        })

The problem is that I'm not actually sure that it's working. There's no real way to verify if the billing address is actually being sent to paypal, and its resulting in me getting processing errors such as this one:

         "processor_response": {
                        "avs_code": "N",
                        "cvv_code": "M",
                        "response_code": "9500"
                    }

I couldn't find anything in the official docs about sending billing addresses during the field submission. If anyone knows anything about this let me know. If it's not possible, I will have to switch to the raw sdk.



Solution 1:[1]

For troubleshooting purposes you should assign the object to a variable and then log it:

let submitOptions = {
            // The full name as shown in the card and billing address
            cardHolderName: cardHolderName,
            billingAddress: billingAddress
        };
console.log('submitOptions',submitOptions,JSON.encode(submitOptions,null,4));
hostedFields
        .submit(submitOptions)

The console output of that is what you should include in your question as an example (the actual data is what's important to check for correctness, not your code and variable names used to create the object)


Here is a working HTML/JS ('raw sdk') sample you can use to check whether the same decline and AVS:N happens independently of react-paypal-js. Trying with this will help you isolate whether it's an issue with that module/its use, vs. with your live account and its processing.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />

  <!-- Sample CSS styles for demo purposes. You can override these styles to match your web page's branding. -->
  <link rel="stylesheet" type="text/css" href="https://www.paypalobjects.com/webstatic/en_US/developer/docs/css/cardfields.css"/>
</head>
<body>

<!-- Include the PayPal JavaScript SDK with correct client-id, and a client_token generated for this checkout-->
<script src="https://www.paypal.com/sdk/js?components=buttons,hosted-fields&amp;client-id=REPLACE_WITH_YOURS" data-client-token="REPLACE_WITH_TOKEN"></script>

<div align="center" style="margin:auto; max-width:600px;">

  <div id="paypal-button-container"></div> 

  <!-- Advanced credit and debit card payments form -->
  <form id="card-form" style="text-align:left; padding:10px;">
    <div align="center" style="padding-bottom:5px;"> or </div>

    <label for="card-number">Card Number</label><div id="card-number" class="card_field"></div>
    <div style="display:flex; flex-direction:row;">
      <div>
        <label for="expiration-date">Expiration Date</label><div id="expiration-date" class="card_field"></div>
      </div>
      <div style="margin-left:10px;">
        <label for="cvv">CSC</label><div id="cvv" class="card_field"></div>
      </div>
    </div>
    <label for="card-holder-name">Name on Card</label>
    <input type="text" id="card-holder-name" name="card-holder-name" autocomplete="off" placeholder="card holder name"/>
    <label for="card-billing-address-street">Billing Address</label>
    <input type="text" id="card-billing-address-street" name="card-billing-address-street" autocomplete="off" placeholder="street address"/>
    <input type="text" id="card-billing-address-unit" name="card-billing-address-unit" autocomplete="off" placeholder="unit"/>
    <input type="text" id="card-billing-address-city" name="card-billing-address-city" autocomplete="off" placeholder="city"/>
    <input type="text" id="card-billing-address-state" name="card-billing-address-state" autocomplete="off" placeholder="state"/>
    <input type="text" id="card-billing-address-zip" name="card-billing-address-zip" autocomplete="off" placeholder="zip / postal code"/>
    <input type="text" id="card-billing-address-country" name="card-billing-address-country" autocomplete="off" placeholder="country code" />

    <div align="center" >
      <button value="submit" id="submit" class="btn" style="width:150px !important;font-size:120%;">Pay</button>
    </div>
  </form>
</div>

<script>
    function createOrderOnServer(data, actions) {
        return fetch('/path/on/your/server/paypal/order/create/', {
            method: 'post'
        }).then(function(res) {
            return res.json();
        }).then(function(orderData) {
            return orderData.id;
        });
    }
 
    function captureOrderOnServer(data, actions) {
        return fetch('/path/on/your/server/paypal/order/' + (data.orderID || data.orderId) + '/capture/', {
            method: 'post'
        }).then(function(res) {
            return res.json();
        }).then(function(orderData) {
            // Three cases to handle:
            //   (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
            //   (2) Other non-recoverable errors -> Show a failure message
            //   (3) Successful transaction -> Show confirmation or thank you

            // This example reads a v2/checkout/orders capture response, propagated from the server
            // You could use a different API or structure for your 'orderData'
            var errorDetail = Array.isArray(orderData.details) && orderData.details[0];

            if (actions && errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
                return actions.restart(); // Recoverable state, per:
                // https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
            }

            if (errorDetail) {
                var msg = 'Sorry, your transaction could not be processed.';
                if (errorDetail.description) msg += '\n\n' + errorDetail.description;
                if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')';
                return alert(msg); // Show a failure message
            }

            // Show a success message, or redirect to success page
            cardDetails = orderData.payment_source && orderData.payment_source.card;
            if (cardDetails) alert('Transaction completed with ' + cardDetails.brand + ' x-' + cardDetails.last_digits);
            else alert('Transaction completed by ' + orderData.payer.name.given_name);
        });
    }

    // Displays PayPal buttons
    paypal.Buttons({
        createOrder: createOrderOnServer,

        onApprove: captureOrderOnServer,

    }).render("#paypal-button-container");

    // If this returns false or the card fields aren't visible, see step #1 in https://developer.paypal.com/docs/business/checkout/advanced-card-payments/
    if (paypal.HostedFields.isEligible()) {

      // Renders card fields
      paypal.HostedFields.render({
        // On submit, calls your server to set up the transaction
        createOrder: createOrderOnServer,

        styles: {
          '.valid': {
           'color': 'green'
          },
          '.invalid': {
           'color': 'red'
          }
        },

        fields: {
          number: {
            selector: "#card-number",
            placeholder: "4111 1111 1111 1111"
          },
          cvv: {
            selector: "#cvv",
            placeholder: "123"
          },
          expirationDate: {
            selector: "#expiration-date",
            placeholder: "MM/YY"
          }
        }
      }).then(function (cardFields) {
        document.querySelector("#card-form").addEventListener('submit', (event) => {
          const submitterHTML = event.submitter.innerHTML;
          event.submitter.innerHTML = 'Processing...'; event.submitter.disabled = true;
          event.preventDefault();

          let submitOptions = {
            // Cardholder's first and last name
            cardholderName: document.getElementById('card-holder-name').value,
            // Billing Address
            billingAddress: {
              // Street address, line 1
              streetAddress: document.getElementById('card-billing-address-street').value,
              // Street address, line 2 (Ex: Unit, Apartment, etc.)
              extendedAddress: document.getElementById('card-billing-address-unit').value,
              // State
              region: document.getElementById('card-billing-address-state').value,
              // City
              locality: document.getElementById('card-billing-address-city').value,
              // Postal Code
              postalCode: document.getElementById('card-billing-address-zip').value,
              // Country Code
              countryCodeAlpha2: document.getElementById('card-billing-address-country').value
            }
          };
          console.log('submitOptions', submitOptions, JSON.encode(submitOptions,null,4));
          cardFields.submit(submitOptions).then(function (data) {
            console.log('Order was created with card', data);

            //Next either capture on the server immediately, or use window.location to go to an order review page
            return captureOrderOnServer(data).then(function() {
              event.submitter.innerHTML = submitterHTML; event.submitter.disabled = false;
            });
         }).catch(function (err) {
           event.submitter.innerHTML = submitterHTML; event.submitter.disabled = false;
           console.log('Hosted Fields submission error', err);
           console.log(JSON.stringify(err,null,2));
           //Show message to buyer about failure or validation problems
           alert('Sorry, your payment could not be processed.\n\n' + JSON.stringify(err,null,2));
         });
        });
      });
    } else {
      console.log('Cannot use advanced card fields! See step #1 in guide for activation, and SDK url must refresh from cache afterward');
      document.querySelector("#card-form").style.display = 'none';
    }
  </script>
</body>
</html>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Preston PHX