Reputation: 1625
After searching a lot of articles i couldn't find a solution for my problem.
I have integrated ApplePay button on my site and successfuly made transactions in sandbox mode. I'm using authorize.net php SDK to generate the request. The problems started when i switched to live. The message from authorize.net is "There was an error processing the payment data. Required fields are missing from decrypted data"
Here what i have done:
Note that if i switch back to sandbox mode, the transaction passes without issues.
The request and the failed response follows:
Request:
{
"createTransactionRequest":{
"merchantAuthentication":{
"name":"xxxxxxxxx",
"transactionKey":"xxxxxxxxxxx"
},
"clientId":"sdk-php-2.0.0",
"refId":"ref1575669789",
"transactionRequest":{
"transactionType":"authOnlyTransaction",
"amount":"14.08",
"payment":{
"opaqueData":{
"dataDescriptor":"COMMON.APPLE.INAPP.PAYMENT",
"dataValue":"eyJ2ZXJzaW9u...Q1OSJ9fQ=="
}
},
"order":{
"invoiceNumber":"63059-191206",
"description":"xxxxxxxxx, xxxxxxxxxxxx v9.0.12 (Order# 63059-191206)"
},
"customer":{
"type":"individual",
"email":""
},
"billTo":{
"firstName":"xxxxxxx",
"lastName":"xxxxxxx",
"address":"xxxx San Remo Cir ",
"city":"Vista",
"state":"CA",
"zip":"92084",
"country":"US"
},
"retail":{
"marketType":0,
"deviceType":8
},
"transactionSettings":{
"setting":[
{
"settingName":"duplicateWindow",
"settingValue":"60"
}
]
}
}
}
}
Response:
{
"transactionResponse":{
"responseCode":"3",
"authCode":"",
"avsResultCode":"P",
"cvvResultCode":"",
"cavvResultCode":"",
"transId":"0",
"refTransID":"",
"transHash":"",
"testRequest":"0",
"accountNumber":"",
"accountType":"",
"errors":[
{
"errorCode":"153",
"errorText":"There was an error processing the payment data. Required fields are missing from decrypted data."
}
],
"transHashSha2":"",
"SupplementalDataQualificationIndicator":0
},
"refId":"ref1575669789",
"messages":{
"resultCode":"Error",
"message":[
{
"code":"E00027",
"text":"The transaction was unsuccessful."
}
]
}
}
What am i missing?
EDIT:
Here is the code regarding sending opaqueData from ApplePay
$transactionMode = $cc_authorize_mode == $this->MODE_TEST ? \net\authorize\api\constants\ANetEnvironment::SANDBOX : \net\authorize\api\constants\ANetEnvironment::PRODUCTION;
$merchantAuthentication = new AnetAPI\MerchantAuthenticationType();
$merchantAuthentication->setName($cc_authorize_loginid);
$merchantAuthentication->setTransactionKey($cc_authorize_txnkey);
// Set the transaction's refId
$refId = 'ref' . time();
$phoneNumber = ! empty($co_b_phone) ? $co_b_phone : $co_phone;
$customerEmail = ! empty($co_b_email) ? $co_b_email : $co_email;
$ip = lloader()->getUtilByName('ip')->getClientIp();
// Create order information
$order = new AnetAPI\OrderType();
$order->setInvoiceNumber($order_number);
$order->setDescription($this->getOrderPostedByMessage($id_order, $order_number));
// Set the customer's Bill To address
$customerAddress = new AnetAPI\CustomerAddressType();
$customerAddress->setFirstName($co_ccholder_firstname);
$customerAddress->setLastName($co_ccholder_lastname);
if (! empty($co_b_company)) { $customerAddress->setCompany($co_b_company); }
$customerAddress->setAddress($co_b_address." ".$co_b_address2);
$customerAddress->setCity($co_b_city);
$bState = f_isUSState($co_b_state) ? $STATES_XX[$co_b_state] : $STATES[$co_b_state];
$customerAddress->setState($bState);
$customerAddress->setZip($co_b_zip);
$customerAddress->setCountry($countriesISO2[$co_country]);
$customerAddress->setPhoneNumber($phoneNumber);
$customerAddress->setEmail($customerEmail);
// Set the customer's identifying information
$customerData = new AnetAPI\CustomerDataType();
$customerData->setType("individual");
if ( ! empty($member_row['id'])) { $customerData->setId($member_row['id']); }
$customerData->setEmail($customerEmail);
// Add values for transaction settings
$duplicateWindowSetting = new AnetAPI\SettingType();
$duplicateWindowSetting->setSettingName("duplicateWindow");
$duplicateWindowSetting->setSettingValue("60");
// Create a TransactionRequestType object and add the previous objects to it
$transactionRequestType = new AnetAPI\TransactionRequestType();
$transactionRequestType->setCustomerIP($ip);
$transactionRequestType->setTransactionType($this->api_trtype_map[$transactionType]);
if (empty($this->applePayPaymentData)) {
// Normal CC request
// Create the payment data for a credit card
...
} else {
$retail = new AnetAPI\TransRetailInfoType();
$retail->setMarketType('0');
$retail->setDeviceType('8');
$transactionRequestType->setRetail($retail);
// Apple Pay Token Request
$op = new AnetAPI\OpaqueDataType();
$op->setDataDescriptor("COMMON.APPLE.INAPP.PAYMENT");
$paymentToken = base64_encode($this->applePayPaymentData);
$op->setDataValue($paymentToken);
$payment = new AnetAPI\PaymentType();
$payment->setOpaqueData($op);
}
$transactionRequestType->setAmount($grandTotal);
$transactionRequestType->setOrder($order);
$transactionRequestType->setPayment($payment);
$transactionRequestType->setBillTo($customerAddress);
$transactionRequestType->setCustomer($customerData);
$transactionRequestType->addToTransactionSettings($duplicateWindowSetting);
// Assemble the complete transaction request
$request = new AnetAPI\CreateTransactionRequest();
$request->setMerchantAuthentication($merchantAuthentication);
$request->setRefId($refId);
$request->setTransactionRequest($transactionRequestType);
// Create the controller and get the response
$controller = new AnetController\CreateTransactionController($request);
$response = $controller->executeWithApiResponse($transactionMode);
if ($response != null) {
if ($response->getMessages()->getResultCode() == "Ok") {
...
if ($tresponse != null && $tresponse->getMessages() != null) {
...
return true;
} else {
if ($tresponse->getErrors() != null) {
...
}
}
...
}
...
}
EDIT2:
I added email and phone and ip address in the request with same result. The modified request follows:
{
"createTransactionRequest":{
"merchantAuthentication":{
"name":"**********",
"transactionKey":"***************"
},
"clientId":"sdk-php-2.0.0",
"refId":"ref1576180306",
"transactionRequest":{
"transactionType":"authOnlyTransaction",
"amount":"14.08",
"payment":{
"opaqueData":{
"dataDescriptor":"COMMON.APPLE.INAPP.PAYMENT",
"dataValue":"eyJ2ZXJzaW9uIj...DFiZiJ9fQ=="
}
},
"order":{
"invoiceNumber":"63117-191212",
"description":"******************* v9.0.12 (Order# 63117-191212)"
},
"customer":{
"type":"individual",
"email":"*********@gmail.com"
},
"billTo":{
"firstName":"Gabe",
"lastName":"Garcia",
"address":"********* Cir ",
"city":"Vista",
"state":"CA",
"zip":"92084",
"country":"US",
"phoneNumber":"**************",
"email":"**********@gmail.com"
},
"customerIP":"************",
"retail":{
"marketType":"0",
"deviceType":"8"
},
"transactionSettings":{
"setting":[
{
"settingName":"duplicateWindow",
"settingValue":"60"
}
]
}
}
}
}
Upvotes: 14
Views: 2726
Reputation: 739
I have solved this, at least for me. The problem was the $op->setDataValue(); field should NOT be just token.paymentData.data . Rather, it shoud be a Base64-encoded JSON string representing the entire token.paymentData object.
Upvotes: 1
Reputation: 1147
This is most probably due to a data issue in OpaqueData field which comes from ApplePay side. So my suggestion is to print that token in logfile, then decrypt the same using one of the following libraries to manually check whether all data present there. You can do the same for both the Sandbox environment and Live environment. So you will see any difference in token data.
https://github.com/PayU-EMEA/apple-pay
https://github.com/etsy/applepay-php
This is how it does using etsy applepay-php library.
You'll need a 'Payment Processing Certificate' and a private key from Apple (referred to as merch.cer and priv.p12 below). You can generate these at Apple's Dev Center. You'll also need an example payment token generated on an end-user device and the timestamp at which it was generated. An RSA-encrypted token should look like this:
{
"data": "<base64>",
"header": {
"applicationData": "<hex_optional>"
"wrappedKey": "<base64>",
"publicKeyHash": "<base64>",
"transactionId": "<hex>"
},
"signature": "<base64>",
"version": "RSA_v1"
}
Demo
$ # Copy in your payment processing cert and test token
$ cd examples
$ cp /secret/place/merch.cer .
$ cp /secret/place/token.json .
$
$ # Extract private key from cert
$ openssl pkcs12 -export -nocerts -inkey merch.key -out priv.p12 -password 'pass:'
$
$ # Get intermediate and root certs from Apple
$ wget -O int.cer 'https://www.apple.com/certificateauthority/AppleAAICAG3.cer'
$ wget -O root.cer 'https://www.apple.com/certificateauthority/AppleRootCA-G3.cer'
$
$ # Verify chain of trust
$ openssl x509 -inform DER -in merch.cer -pubkey > pub.pem
$ openssl x509 -inform DER -in root.cer > root.pem
$ openssl x509 -inform DER -in int.cer > int_merch.pem
$ openssl x509 -inform DER -in merch.cer >> int_merch.pem
$ openssl verify -verbose -CAfile root.pem int_merch.pem # should output OK
$
$ # Run demo
$ cd ..
$ php -denable_dl=on -dextension=`pwd`/modules/applepay.so examples/decrypt.php -p <privkey_pass> -c examples/token.json -t <time_of_transaction>
Upvotes: 3
Reputation: 28413
As mentioned here
A couple of things to look at:
- The Apple Merchant ID that you enter into our site must be identical to the one that you created at the Apple site. If it is different, we will not be able to to decrypt the payment data.
- Must be an e-commerce transaction. Confirm that your gateway account is set up as a card-not-present account.
- The submitted data must be base64 encoded. As far as I can tell, you're doing that right, but double-check. I don't know if the BLOB
you're getting back is already base64 encoded, but maybe double-check to make sure you're not double-encoding it.
token.paymentData.data
. Rather, it shoud be a Base64-encoded
JSON string representing the entire token.paymentData object
. There was an error processing the payment data.
Upvotes: 1