cancel
Showing results for 
Search instead for 
Did you mean: 

Unclear basic workflow documentation - Accept.js

Where can I get examples of how to pull user payment info (from Authorize.net payment profile) using Accept.js (client side), so that said info never touches the server?

Then, how do I edit/save the payment info using Accept.js

 

Also, how do I create a new payment profile using Accept.js, again, so that the info never touches the server?

 

The documentation provided by Authorize.net is very jumbled and incomplete, with not a lot of coherence. I've read, re-read and scratched my head. Also, emailing support isn't really any help either, because all htey do is reply with cannexd responses that are links to the disjointed documentation that I've already read (and that didn't help).

 

Thank you.

jima
Regular Contributor
21 REPLIES 21

There are examples of both creating a customer payment profile and updating a customer payment profile in our API Reference (on the "Try It" tab). Either of those methods work equally fine with a payment nonce.

 

Creating a payment profile would ordinarily look like this:

 

<?xml version="1.0" encoding="utf-8"?>
<createCustomerPaymentProfileRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
  <merchantAuthentication>
    <name>API_LOGIN_ID</name>
    <transactionKey>API_TRANSACTION_KEY</transactionKey>
  </merchantAuthentication>
  <customerProfileId>10000</customerProfileId>
  <paymentProfile>
    <billTo>
      <firstName>John</firstName>
      <lastName>Doe</lastName>
      <company></company>
      <address>123 Main St.</address>
      <city>Bellevue</city>
      <state>WA</state>
      <zip>98004</zip>
      <country>USA</country>
      <phoneNumber>000-000-0000</phoneNumber>
      <faxNumber></faxNumber>
    </billTo>
    <payment>
      <creditCard>
        <cardNumber>4111111111111111</cardNumber>
        <expirationDate>2023-12</expirationDate>
      </creditCard>
    </payment>
    <defaultPaymentProfile>false</defaultPaymentProfile>
  </paymentProfile>
  <validationMode>liveMode</validationMode>
</createCustomerPaymentProfileRequest>

If you used a nonce instead, it would look like this:

 

<?xml version="1.0" encoding="utf-8"?>
<createCustomerPaymentProfileRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
  <merchantAuthentication>
    <name>API_LOGIN_ID</name>
    <transactionKey>API_TRANSACTION_KEY</transactionKey>
  </merchantAuthentication>
  <customerProfileId>10000</customerProfileId>
  <paymentProfile>
    <billTo>
      <firstName>John</firstName>
      <lastName>Doe</lastName>
      <company></company>
      <address>123 Main St.</address>
      <city>Bellevue</city>
      <state>WA</state>
      <zip>98004</zip>
      <country>USA</country>
      <phoneNumber>000-000-0000</phoneNumber>
      <faxNumber></faxNumber>
    </billTo>
    <payment>
      <opaqueData>
        <dataDescriptor>COMMON.ACCEPT.INAPP.PAYMENT</dataDescriptor>
        <dataValue>9471056021205027705001</dataValue>
      </opaqueData>  
    </payment>
    <defaultPaymentProfile>false</defaultPaymentProfile>
  </paymentProfile>
  <validationMode>liveMode</validationMode>
</createCustomerPaymentProfileRequest>

 

 


@jima wrote:

I'm still seeing an issue with payment info still being passed through our server though. What am I missing (when relating it to the 2 use case examples I previously provided)?


 

When using Accept.js, you do need to make sure your craft your form in such a way that it's not just submitting every form field straight to your server. If you have the card data and everything else all integrated into one form, you need to set the submit button up so that it sends card data through the Accept.js script, but then takes the nonce and submits that to your server with only the other fields in the form.

 

The first key thing to do is to make sure that any "submit" button on the form does not actually have the attribute type="submit", so the browser's not trying to actually post it when clicked. We usually recommend just making a "button" type like this:

<button type="button" id="submitButton" onclick="sendPaymentDataToAnet()">Pay</button>

Then, your "sendPaymentDataToAnet()" function pulls out the card data and sends it through Accept.js to get the nonce (like in step 3 in our Accept.js documentation).

 

Once you have the nonce, then it's up to you to determine the best way to get the rest of the information to your server, along with the nonce. We provide an example of that in step 4 in our Accept.js documentation. That example is using JavaScript to programatically create a new form within the DOM for that page, create input parameters for all of the non-card fields on your existing form and the payment nonce, and then submitting that new form to your server.

 

 

 


@jima wrote:

... here's a basic starting point. In this particular example, there is no need to perform any sort of charge/authorization transaction, it simply needs to be able to populate pre-saved payment info into a form (our form), and display it to the user so that they can review it, and update it if necessary.

 

Basic use case:

User logs into the system and visits their user profile (where they have stored their payment info). In the profile section, they have the option to review and/or update their creadit card info.

 

So, given that (first) basic scenario, how can we  - using Accept.js - load the form with the pre-saved payment info so that they can review it? I understand that I can use the API to pull that info, I've done that, but doing that means the payment info (while obfuscated) touches our server, which we want to not do.

 

After loading the data into the form, how then do we update said payment data, should the user need to make changes? Through the nonce token, right? 


 

Let me try to address this scenario specifically, and give a walkthrough of how I might go about implementing that.

 

The key part of all of this would be Customer Profiles. We store payment info on our side, so you don't have to store it on your side. You have an API to use to store the info, update the info, retrieve the info, or use the info in a transaction. That API for creation and updating works with actual card numbers, but since you don't want to be exposed to those, it also works with the payment nonce generated by Accept.js. For even less exposure, you can embed or redirect the Accept Customer forms so that creation or updating piece happens within our form.

 

Let's assume for this example that you want all of the forms hosted on your side, though.

 


jima wrote: 

User logs into the system and visits their user profile (where they have stored their payment info). In the profile section, they have the option to review and/or update their creadit card info. 


 

Here's what I would do. Once the user logs into the system, I know who they are on my side, and I would know what customer profile ID corresponds to them. I'd have an extra table in my database with that info, or I'd have some separate lookup table with that info.

 

When the user navigates to the "user profile" page on my site, I'd do an API call to "getCustomerProfileRequest" to retrieve the details of that customer's profile. That call will return all of the info about the customer that I have stored at Authorize.Net, including any associated payment profiles and shipping address profiles.

 

I'd then format that data to present to the customer. I could list out all of the details for each payment profile right on that page, or I could just list the card type and last four or something and require a press on a "Details" button to show more info. I'd put an "Add new payment profile" button somewhere (as well as an "Add new shipping address" button), and then put an "Edit" button next to each payment profile or address profile I have displayed.

 

If the customer clicks on "Add new payment profile", I would send him/her to a form hosted on my server which would take as input all of the needed information for the new payment profile (like card information, billing address, etc). When the customer clicks the submit or done button, I'd send the card info through Accept.js to get a nonce. I'd take the nonce and the other data (like the example in my previous post) and then submit that to a location on my server, where my application would package the nonce and other data into a createCustomerPaymentProfileRequest API call.

 

If the customer had instead clicked on the "Edit" button for an existing payment profile, the process would be similar. I would bring them to a form pre-populated with everything that was already on file for that payment profile. I could either use the information I got back in that first getCustomerProfileRequest call, or, to be extra sure I had the most current information, I could do a call to getCustomerPaymentProfileRequest just for that profile, and use the information returned to populate the fields.

 

When the customer clicks the submit or done button there, I'd first want to see if they updated the card data. If they did (card number or expiration date), I'd send that through Accept.js to get a nonce. If they didn't, I can skip that step and just post everything as is to my server. My server would then do a call to updateCustomerPaymentProfileRequest. To update a customer payment profile, you have to send all the data you want associated with that payment profile, not just the data that was changed. Any thing that was in the profile before that's not sent in the update call is blanked out. If the payment data had been changed, you'd send the nonce in that call. If the payment data hadn't changed, I'd still need to send the old payment data so that the existing payment data didn't get erased. To do that, I'd just send the same obfuscated card number and expiration date that was returned to me in the call to getCustomerPaymentProfileRequest.

 


@jima wrote:

I understand that I can use the API to pull that info, I've done that, but doing that means the payment info (while obfuscated) touches our server, which we want to not do.

 


 

When you use the API to pull the info from a payment profile, you're only getting a masked card number and masked expiration date back, like this:

 

        <payment>
            <creditCard>
                <cardNumber>XXXX1111</cardNumber>
                <expirationDate>XXXX</expirationDate>
                <cardType>Visa</cardType>
            </creditCard>
        </payment>

 

You've got only the last four so that you can present that to the customer to identify their card. There's nothing to worry about regarding having that on your server. You also need that last four to be able to do an update, since all of the existing info needs to be resent in an update request. In an update request, if you send the masked info as you've received it ("XXXX1111"), our system checks the last four you send against the last four on file, and if it matches will leave the card number intact on our system.

Thanks for this... that helps a lot, though I still don't really love the fact that there isn't an Accept.js method for simply pulling payment info, or adding, or updating for that matter. Seems like having to juggle the various integration methods only complicates and convolutes the process. Do you know if there are plans, woth hard timelines perhaps, to expand the capabilities of Accept.js to include those types of calls?

jima
Regular Contributor

No plans, although that's another thing that would be worth posting on our Ideas Forum. Basically, looking for the ability to not just send a credit card to Accept.js and get a nonce, but send a credit card and a profile ID, with a command to add the card to the provided profile, or update a specified profile with that card.

 

I can definitely see the usefulness of such an idea, but like I said, we have no current plans for that. Again, if you post that in the Ideas Forum, we can track that for future improvements.

Following the previous suggestion of getting a nonce through the Accept.js, then using it with an server call, can someone explain why the XML generation in the PHP SDK is messing up?

 

 

Here's the SDK method call to createCustomerPaymentProfile():

 

$authNetCusObj = new AuthorizeNetCIM();
$authorizeDotNetResponse = $authNetCusObj->createCustomerPaymentProfile($custProfId, $formattedRequestData);

 

Here's the PHP array that's being passed into (which the XML is supposed to get generated from):

 

$formattedRequestData = array(
	'billTo' => array(
		'firstName' => 'TestKen',
		'lastName' => 'TestBraz',
		'address' => '123 Testa Trove',
		'city' => 'Hahahah',
		'state' => 'Utah',
		'zip' => '84440'
	),
	'payment' => array(
		'opaqueData' => array(
			'dataDescription' => 'COMMON.ACCEPT.INAPP.PAYMENT',
			'dataValue' => trim($data['dataNonce'])
		)
	)
);

 

Here's the (bad) XML that gets generated in the call:

 

<?xml version="1.0" encoding="utf-8"?>
<createCustomerPaymentProfileRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
	<merchantAuthentication>
		<name>Protected</name>
		<transactionKey>Protected</transactionKey>
	</merchantAuthentication>
	<customerProfileId>181</customerProfileId>
	<paymentProfile>
		<billTo>
			<0>TestKen</0>
		</billTo>
		<billTo>
			<0>TestBraz</0>
		</billTo>
		<billTo>
			<0>123 Testa Trove</0>
		</billTo>
		<billTo>
			<0>Test City</0>
		</billTo>
		<billTo>
			<0>Test State</0>
		</billTo>
		<billTo>
			<0>84440</0>
		</billTo>
		<payment>
			<dataDescription>COMMON.ACCEPT.INAPP.PAYMENT</dataDescription>
			<dataValue>eyJjfMl</dataValue>
		</payment>
	</paymentProfile>
</createCustomerPaymentProfileRequest>

 

jima
Regular Contributor

HI @jima

 

 

Two things:

 

1. The AuthorizeNetCIM.php class is still in the SDK for backward compatibility, but it's a relic from when the customer profiles functions were completely separate from the rest of our API. Now, all the payment functions and profile functions and everything else lives in one unified API called, appropriately enough, the Authorize.Net API. So, going forward, if you're wanting to do things with customer profiles, better to use one of the methods in the current API.

 

$authNetCusObj= new AnetAPI\CreateCustomerProfileRequest();

2. The PHP SDK has a bunch of setter and getter functions for working with the various objects instead of having you write them into an array. For example, setting the billTo address would look like this:

 

	  // Create the Bill To info
	  $billto = new AnetAPI\CustomerAddressType();
	  $billto->setFirstName("Ellen");
	  $billto->setLastName("Johnson");
	  $billto->setCompany("Souveniropolis");
	  $billto->setAddress("14 Main Street");
	  $billto->setCity("Pecan Springs");
	  $billto->setState("TX");
	  $billto->setZip("44628");
	  $billto->setCountry("USA");

Take a look at the sample code on GitHub for a complete example of how to create a profile with a payment nonce/token.

 

Understood that the SDK is older, but until the API is installable without the current necessary dependencies, that isn't an option (which I think I initially stated in the beginning of this thread.) I don't have full admin rights to the machine(s) to install the necessay dependencies (and those that do have access have denied my request to have them added).

 

So I'm stuck here, with my only option to be to use the SDK.

 

BTW: I've also used the setter functions as well (instead of the array)... it made no difference, they both fail in their own ways.

 

 

jima
Regular Contributor

So which version of the SDK are you using?

LOL, ashamed to admit that I don't know and that I could use some help in finding out too! :)

 

Here's the file list that I have, if that helps any (which it may not):
AuthorizeNetCIM.php

AuthorizeNetException.php

AuthorizeNetRequest.php

AuthorizeNetResponse.php

AuthorizeNetTypes.php

AuthorizeNetXMLResponse.php

(None of them reference a version anywhere within)

jima
Regular Contributor