14

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:

  1. Changed the payment processing certificate with one from live authorize.net account
  2. Changed the credentials i'm using to process authorize.net payments to the same live account i got the payment process certificate
  3. Use live apple device with real credit card.
  4. I'm using First data Nashville processor as CC processor which support ApplePay

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"
                }
            ]
        }
    }
}

}

bksi
  • 1,543
  • 1
  • 22
  • 39
  • 1
    Tried to regenerate certificates? – Mully Dec 10 '19 at 09:45
  • 1
    Yes, i regenerated the payment processing certificates dozen times, even recreated merchant identity in apple account. – bksi Dec 10 '19 at 21:44
  • 1
    transactionRequest -> customer -> email is empty, it may be required to be set, is it possible to be set at the request? – Jannes Botis Dec 11 '19 at 22:09
  • 1
    Can you post the code related to setting "opaqueData" field? Where it should be the base64 encoded token received from ApplePay wallet. – DinushaNT Dec 12 '19 at 06:24
  • @DinushaNT i added the code you asked for. Note that same code works with sandbox without any issues. – bksi Dec 12 '19 at 18:37
  • @JannesBotis i tested it with populated mail with same result. It shouldn't be required, because it comes directly from apple device where it is not required. – bksi Dec 12 '19 at 18:38
  • Your code here consists of two parts. The connection between them is unclear. To a second or third party, your code here is insufficient to solve this. – Roadowl Dec 12 '19 at 19:08
  • 2
    @Roadowl what is the matter. I edited the post. Note that same code works in sandbox mode. Also the request is generated, and can be seen. It does not much with how it is generated i think. – bksi Dec 12 '19 at 20:09
  • Looks like setting the opaquedata is correct. Since this is working correctly in sandbox, please check whether you have the same merchant id in live also. Because the certificates are associated with applepay merchant id. – DinushaNT Dec 13 '19 at 02:07
  • Try from the Merchant Interface, click on Settings and Profile -> Payment Form -> Form Fields and see if any of the fields are required. – Jannes Botis Dec 14 '19 at 13:35
  • @JannesBotis there are no additional required fields there. – bksi Dec 15 '19 at 17:12

2 Answers2

3

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>
DinushaNT
  • 1,089
  • 6
  • 15
0

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.
  • opaqueData field should NOT be just 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.

  • Both opaque parameters must be specified.
  • You cannot include card number or expiration date.
  • You cannot include track data.
  • Must be E-commerce transaction. Confirm that your gateway account is set up as a Card Not Present account.
  • The transaction must be authorization or authorize and capture type of transaction.
  • You cannot include 3DS data.
  • You must submit data that can be successfully decrypted.
  • Decrypted data must belong to the merchant submitting the request.
  • The submitted data must be base64 encoded.
Vignesh Kumar A
  • 26,578
  • 11
  • 57
  • 101
  • Thanks for the suggestions. All of them are applied. I'm not sure you noted that all transactions passes when in sandbox mode. I made fully new merchant id and used it for the transactions with same result. Then i tried same id back on sandbox and the transactions are passing. – bksi Dec 13 '19 at 14:32
  • @bksi I have updated answer. Please make sure you have completed all the checklists and still you facing the issue then try *Re-doing the whole process from creating new bundle identifier,merchant id,registering it to bundle id & on authorize portal,generating a new CSR from Authorize portal and creating new payment processing certificate on apple developer & using 3DS payment type* – Vignesh Kumar A Dec 15 '19 at 04:28