Using the Authorize.net API to set up Automated Recurring Billing, the ARB is set up correctly when credit card info is input on our view. If, however, we allow the user to select a card within their profile, we get a parsing error in the response.
This is happening in both the sandbox and operational environment. We are using the latest Java SDK, Java 8 and Tomcat 8.
The code to create a subscription and asscociate it with an existing profile is based on the sample code in the developer documentation. The ccp object in the code below is our own object that contains the customerProfileid, customerPaymentProfileid, and the customerAddressid, which we harvest upstream.
Here is the stack trace, and after that, the code.
[INFO,XmlUtility] Exception - while deserializing text:'<?xml version="1.0" encoding="utf-8"?><ErrorResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd"><messages><resultCode>Error</resultCode><message><code>E00003</code><text>The element 'creditCard' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd' has invalid child element 'expirationDate' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'. List of possible elements expected: 'cardNumber' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'.</text></message></messages></ErrorResponse> ' [WARN,XmlUtility] Exception Details-> Code:'null', Message:'unexpected element (uri:"AnetApi/xml/v1/schema/AnetApiSchema.xsd", local:"ErrorResponse"). Expected elements are <{AnetApi/xml/v1/schema/AnetApiSchema.xsd}ARBCancelSubscriptionResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}ARBCreateSubscriptionResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}ARBGetSubscriptionListResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}ARBGetSubscriptionResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}ARBGetSubscriptionStatusResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}ARBUpdateSubscriptionResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}authenticateTestResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}createCustomerPaymentProfileResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}createCustomerProfileResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}createCustomerProfileTransactionResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}createCustomerShippingAddressResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}createTransactionResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}decryptPaymentDataResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}deleteCustomerPaymentProfileResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}deleteCustomerProfileResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}deleteCustomerShippingAddressResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getAUJobDetailsResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getAUJobSummaryResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getBatchStatisticsResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getCustomerPaymentProfileListResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getCustomerPaymentProfileResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getCustomerProfileIdsResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getCustomerProfileResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getCustomerShippingAddressResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getHostedPaymentPageResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getHostedProfilePageResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getMerchantDetailsResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getSettledBatchListResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getTransactionDetailsResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getTransactionListResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}getUnsettledTransactionListResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}isAliveResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}logoutResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}mobileDeviceLoginResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}mobileDeviceRegistrationResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}securePaymentContainerResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}sendCustomerTransactionReceiptResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}updateCustomerPaymentProfileResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}updateCustomerProfileResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}updateCustomerShippingAddressResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}updateHeldTransactionResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}updateMerchantDetailsResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}updateSplitTenderGroupResponse>,<{AnetApi/xml/v1/schema/AnetApiSchema.xsd}validateCustomerPaymentProfileResponse>'
net.authorize.api.contract.v1.CustomerProfileIdType profile=null; PaymentScheduleType schedule = new PaymentScheduleType(); PaymentScheduleType.Interval interval = new PaymentScheduleType.Interval(); interval.setLength((short)intervalLength); interval.setUnit(intervalType); schedule.setInterval(interval); GregorianCalendar cal=new GregorianCalendar(); cal.setTime(start); schedule.setStartDate(DatatypeFactory.newInstance().newXMLGregorianCalendar(cal)); schedule.setTotalOccurrences((short)totalPayments); schedule.setTrialOccurrences((short)0); if(ccp!=null) { profile = new net.authorize.api.contract.v1.CustomerProfileIdType(); profile.setCustomerProfileId(String.valueOf(ccp.getCustomerProfileid())); profile.setCustomerPaymentProfileId(String.valueOf(ccp.getPaymentProfileid())); profile.setCustomerAddressId(ccp.getCustomerAddressid()); // cannot find how to retrieve customerAddressID from Authorize.net } ARBSubscriptionType arbSubscriptionType = new ARBSubscriptionType(); arbSubscriptionType.setPaymentSchedule(schedule); arbSubscriptionType.setAmount(amountPerInterval.getBigDecimal()); arbSubscriptionType.setTrialAmount(Money.ZERO.getBigDecimal()); arbSubscriptionType.setPayment(pmt); arbSubscriptionType.setOrder(order); if(ccp!=null) { arbSubscriptionType.setProfile(profile); } NameAndAddressType name = new NameAndAddressType(); name.setFirstName(customer.getName()); name.setLastName(customer.getSurName()); arbSubscriptionType.setBillTo(name); ARBCreateSubscriptionRequest apiRequest = new ARBCreateSubscriptionRequest(); apiRequest.setSubscription(arbSubscriptionType); ARBCreateSubscriptionController controller = new ARBCreateSubscriptionController(apiRequest); controller.execute(); ARBCreateSubscriptionResponse response = controller.getApiResponse(); checkResponse(response);
Solved! Go to Solution.
โ12-27-2017 10:50 AM
I have found the answer to my question. When setting up an ARB using an existing customer profile you must not include any PaymentType, Order, or BillTo properties in your request.
As you might deduce from my code, I was using the same constructor to create a subscription, and then building a profile if my profile object was not null, but in so doing I left in these lines of code
arbSubscriptionType.setPayment(pmt); arbSubscriptionType.setOrder(order);
and I left in
NameAndAddressType name = new NameAndAddressType(); name.setFirstName(customer.getName()); name.setLastName(customer.getSurName()); arbSubscriptionType.setBillTo(name);
These should NOT be part of a createsubscription request when it is being created from an existing profile.
To clarify things in my code, I have created two separate wrapper methods to construct the create subscription request, one to create a subscription with a fresh set of payment info, and the other that leaves those things out but contains the reference to the existing profile.
As it happens, if you leave out the order and payment setters and leave in the billto segment, Authorize.net will return a clear response with a helpful error message that says something like "BillTo should not be used along with profile info". At least that points you somewhere. However, if you include the payment and order, all you get is a parsing error.
Many thanks to @rajvpate for helping me get on the right track to understanding my error.
โ12-29-2017 08:15 AM
Hi @museminder
I tried out the API for create a subscription from customer profile in sandbox environment and got the response as follows:
Request:
<ARBCreateSubscriptionRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
<merchantAuthentication>
<name>78BZ5Xprry</name>
<transactionKey>8s2F95Q7brhHd7Tn</transactionKey>
</merchantAuthentication>
<refId>123456</refId>
<subscription>
<name>Sample subscription</name>
<paymentSchedule>
<interval>
<length>1</length>
<unit>months</unit>
</interval>
<startDate>2020-08-30</startDate>
<totalOccurrences>12</totalOccurrences>
<trialOccurrences>1</trialOccurrences>
</paymentSchedule>
<amount>10.29</amount>
<trialAmount>0.00</trialAmount>
<profile>
<customerProfileId>39931060</customerProfileId>
<customerPaymentProfileId>36223863</customerPaymentProfileId>
<customerAddressId>37726371</customerAddressId>
</profile>
</subscription>
</ARBCreateSubscriptionRequest>
Response:
<?xml version="1.0" encoding="utf-8"?>
<ARBCreateSubscriptionResponse xmlns="AnetApi/xml/v1/schema/
AnetApiSchema.xsd">
<refId>123456</refId>
<messages>
<resultCode>Ok</resultCode>
<message>
<code>I00001</code>
<text>Successful.</text>
</message>
</messages>
<subscriptionId>100748</subscriptionId>
<profile>
<customerProfileId>39931060</customerProfileId>
<customerPaymentProfileId>36223863</customerPaymentProfileId>
<customerAddressId>37726371</customerAddressId>
</profile>
</ARBCreateSubscriptionResponse>
So, the API is able to process the request if values are set correctly.
It seems like there are some values in your ccp object that may not have been set correctly.
Could you send the request object that is getting constructed before you call the controller ?
Also if possible, please share your code for creating the ccp object.
โ12-27-2017 11:21 AM
I wonder if the issue is that I am sending a null string as CustomerAddressID. I have also tried to send an empty string and still it fails. It is not clear to me how I can retrieve the CustomerAddressID from Authorize.net. I have the customer profileid and the payment profile id, and I have seen how I can recover the street address, city, etc, but that customeraddressid is buried deeply.
Must we supply a value for customerAddressid and if so, how to retrieve it?
โ12-27-2017 01:12 PM
Actually, I do have more detail here. Here is how I am trying to retrieve the customer address id:
private void arrangeViewExistingCustomer() throws GatewayException, EncryptionException, UserException { //cpm is CustomerProfileMaskedType, a class property because it is used when the card is selected AuthorizeAPI cardHandler=new AuthorizeAPI(gatewayKey, payNowBase.getHolderPerson(), ValidationModeEnum.LIVE_MODE); String cpiidStr = String.valueOf(cpi.getCustomerProfileid()); cpm = cardHandler.fetchCustomerProfile(cpiidStr); List<CustomerAddressExType> addList = cpm.getShipToList(); for(CustomerAddressExType cat:addList) { customerAddressid=cat.getCustomerAddressId(); } // populate the radio buttons that will show the cards in the profile refreshPaymentProfileDisplay(); }
โ12-27-2017 01:18 PM
As per the spec details here: https://developer.authorize.net/api/reference/index.html#recurring-billing-create-a-subscription-fro..., the customerAddressId is not a required field.
I am wondering, if you could run a debug session and send across the request that gets created for create arb subscription.
The error that you are seeing, is for invalid element expirationDate, so I want to know how the customer profile was created. Please share details on that and we can look into it further.
โ12-27-2017 02:17 PM
You are most kind.
I'm not sure how to print out the details of the ARBCreateSubscriptionRequest. If I use .toString() on the request (as you see below) I just get an Object, not the details.
The error we are discussing occurs at the line below that says
controller.execute();
Here is the code in question:
ARBCreateSubscriptionRequest apiRequest = new ARBCreateSubscriptionRequest(); apiRequest.setSubscription(arbSubscriptionType); System.out.println(apiRequest.toString()); ARBCreateSubscriptionController controller = new ARBCreateSubscriptionController(apiRequest); controller.execute(); ARBCreateSubscriptionResponse response = controller.getApiResponse(); checkResponse(response);
โ12-27-2017 03:29 PM
I have stepped through the createSubscription code and gotten to the point where the apiRequest is passed as a parameter in this line:
ANetApiResponse httpApiResponse = HttpUtility.postData(environment, this.getApiRequest(), this.responseClass);
At this point, the ApiRequest has the attributes that we passed in as we built the request. However, under apiRequest.subscription.payment.creditcard all of the values are null except expiration date, which is "00/0". That is the only place that I have found where there is any reference to expiration date.
Is the parser choking on that string?
I have verified that the customerprofileid and the paymentprofileids are valid, and that the expiration date is valid in the CIM record.
โ12-28-2017 11:01 AM
Also, you asked to see how the credit card profile object is created(here the object is called tempProfile, later referred to as ccp). Here is that code snippet (the first part of the loop that iterates over the list of CustomerPaymentProfileMaskedType).
In the processor that creates subscriptions I don't see anywhere where the expiration date is passed into the request from this object.
for(CustomerPaymentProfileMaskedType cpmt:authCCList) { String cardString = cpmt.getPayment().getCreditCard().getCardNumber(); cardString=cardString.substring(cardString.length()-4); // just print the last four numbers, not the x's String expString = cpmt.getPayment().getCreditCard().getExpirationDate(); // capture expiration date if it hasn't been written into the table long pmtProfileid=Long.valueOf(cpmt.getCustomerPaymentProfileId()); boolean primary=false; CreditCardProfile tmpProfile=new CreditCardProfile( Long.valueOf(cpm.getCustomerProfileId()), // retrieving from cpm because sometimes the cpmt customerid is null pmtProfileid, " card ending in "+cardString, // look -- there is a space there so that the alphabetization works expString, primary, customerAddressid);
โ12-28-2017 11:10 AM
Per your request, here is the request. I can see that the expiration date is "00/0", which is the value I saw when reviewing the objects. I do not see anywhere in the sample code where that might be set from our end. In fact, it doesn't make sense for us to have to set that, as we already provide the customerProfileid and the paymentProfileid. These should be sufficient to create a new subscription with an existing customer profile, as the expiration date can be accessed on your end.
So is this a bug or is there something missing in the documentation, or am I missing something myself?
Here is the request:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ARBCreateSubscriptionRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd"> <merchantAuthentication> <name>7LWyT4suu2L</name> <transactionKey>3pKA5937S3kZ9HnY</transactionKey> </merchantAuthentication> <clientId>sdk-java-1.9.4</clientId> <subscription> <paymentSchedule> <interval> <length>1</length> <unit>months</unit> </interval> <startDate>2017-12-31-08:00</startDate> <totalOccurrences>8</totalOccurrences> <trialOccurrences>0</trialOccurrences> </paymentSchedule> <amount>40.00</amount> <trialAmount>0.00</trialAmount> <payment> <creditCard> <expirationDate>00/0</expirationDate> </creditCard> </payment> <order> <invoiceNumber>1514495180196</invoiceNumber> <description>Invoice from Euterpe Conservatory of Music</description> </order> <billTo> <firstName>Mark</firstName> <lastName>Campbell</lastName> </billTo> <profile> <customerProfileId>38973215</customerProfileId> <customerPaymentProfileId>35424207</customerPaymentProfileId> </profile> </subscription> </ARBCreateSubscriptionRequest>
โ12-28-2017 01:26 PM
The difference between your sample request and the request generated in my process is that the following elements are included between the <trialAmount> segment and the <profile> segment.
This is what's confusing the parser.
Here is the offending segment:
<payment> <creditCard> <expirationDate>00/0</expirationDate> </creditCard> </payment> <order> <invoiceNumber>1514495180196</invoiceNumber> <description>Invoice from Euterpe Conservatory of Music</description> </order> <billTo> <firstName>Mark</firstName> <lastName>Campbell</lastName> </billTo>
โ12-28-2017 01:40 PM