Apple Pay for the Web Merchant Validation

Has anyone found documentation on actually performing the Merchant Validation step? A signed request to the validationURL in the onvalidatemerchant event gives me an error message requesting a valid JSON object. Passing a valid JSON object responds with

{
  "statusMessage": "Payment Services Exception Invalid session request -- missing merchant identifier",
  "statusCode": "400"
}

Unfortunately, all of the obvious key names for merchant identifier are failing me(id, merchant, merchant_id, merchant_identifier). As far as I can tell there, isn't any documentation surrounding this step.

Accepted Reply

Finally figured it out!

"merchantIdentifier" is the correct field name, and there are 2 more required, "domainName" and "displayName". The trick here is that "merchantIdentifier" is actually OID 1.2.840.113635.100.6.32 in the identity certificate(View the certificate in Keychain for an easy way to find this), and "displayName" is your plaintext name.

{
    "merchantIdentifier":"F3[...]4B",
    "domainName":"[verified domain]",
    "displayName":"merchant.[...]"
}

IE:

curl --data '{"merchantIdentifier":"F3[...]4B", "domainName":"[verified domain]", "displayName":"merchant.[...]"}' --cert ./<certFile>:<certPassword>  <validationURL>


And a session token is yours!

Replies

1. Create a 2048 bit Private Key and .csr (rename the extension to .certSigningRequest) using OpenSSL tool,

2. Upload it to the Merchant ID using Create Certificate in apple developer portal

3. Get the merchant_id.cer from developer portal and convert it to .pem using open ssl

3. Added public key of your private key to the .pem file

4. Using .pem and private key, create .p12 (again using openssl).

5. Import .p12 to keystore


Note: the openssl should be replaced with commercial certificate tools as openssl is prone to hacks or security vulnerabilities.


Verify using curl --data '{"merchantIdentifier":"F3[...]4B", "domainName":"[verified domain]", "displayName":"merchant.[...]"}' --cert ./<certFile>:<certPassword> <validationURL>

Thanks raghuram for detail information.

I am able to make a curl request with the merchant certificate in .p12 format, but I am receiving the following JSON response from https://apple-pay-gateway.apple.com



{

"statusMessage": "Payment Services Exception merchantId=C4450CDAB91279A7CB97F5EF9.....0836BB76FCCC7B70BBC9F897 not registered for service",

"statusCode": "417"

}

Your merchant identifier should be as per the human readable one you registered with apple, probably starting with "merchant." I've put an end to end working implementation here https://github.com/norfolkmustard/ApplePayJS

The JSON payload I am sending is:


curl --data '{"merchantIdentifier":"merchant.com.skymode", "domainName":"www.skymode.com", "displayName":"SkyModeMe"}' --cert /Users/ericv/workspaces/skymodefrontend/src/SkyModeFrontEnd/Resources/apple-pay-skymode-merchant-certificate.p12:thepassword https://apple-pay-gateway.apple.com/paymentservices/startSession -v


The responsed back JSON payload that contains a alphanumeric ID string is being extracted from Apple via the .p12 certificate. Turns out, it doesn't matter what value I use for the "merchantIdentifier" json property, I always get back the same HTTP 417 Expectation Failed.

we need some listing of these apple statusCode


I tried removing the MerchantDomain associated with my MerchantID, and got exactly the same " .... not registered for service" text description as you, but got a statusCode of 400 instead of your 417. Knowing what 417 means in detail would seem to be the key.


You're testing this all from the same server you verified with apple?

A list of status codes would be helpful.


I am actually executing the curl request from my development machine, which is from an IP address (not accessible via any domain name) for testing/development purposes. I realize that Apple documentated to make the merchant validation request from the production webserver, but why and how would they enforce that? It seems that all the security checks is in the fact that the SSL certificate passed with every HTTP POST request will be validated and that is all Apple should be relying on (this isn't an oAuth 2.0 callback architecture). The certificate is valid, because if it wasn't I would even get back a HTTP response at all.

You have the possible issue that your dev server isn't using the same approved TLS version or cipher suits as the prod server/site you got accepted by apple, although curl could be using a different cipher compared to apache anyway.


perhaps try some verbose loging for the curl connection, see what headers/cipher etc it's using

Maybe the issue is that I have not signed up for Apple Pay with a credit card gateway. I was assuming I could get a merchant session id in the sandbox environment, which I swore I tried out earlier by making a HTTP POST request to (notice the -cert in the subdomain): https://apple-pay-gateway-cert.apple.com/paymentservices/startSession


and it didn't work. But viola, it works today. I just got back a JSON payload from the Apple Pay sandbox server of:


{"epochTimestamp":1470959853352,"merchantSessionIdentifier":"cd1c24566bbd44bbb531042748b6ec...e5a943f0f94927c24","nonce":"27d89549","merchantIdentifier":"C4450CDAB91279A7CB97F5EF91F0...36BB76FCCC7B70BBC9F897","domainName":"www.skymode.com","displayName":"SkyModeMe","signature":"308006092a864886f70d010702a0803080020101310f300d06096086480165030402010500308006092a864886f70d0107010000a0...9317765c3000000000000"}


I believe the Apple Pay for Web production web servers are returning the status code of 417 because I don't have a "real" credit card gateway for Apple Pay established for my Apple Developer account.

I did try out your php solution in github on my Mac OS X (El Capitan) using the default apache server and the php modules that shipped with Mac OS X. I couldn't get it to work because of the following:


executing apple_pay_comm.php with &u=https://apple-pay-gateway-cert.apple.com/paymentservices/startSession{"curlError":"SSL: Can't load the certificate "/Library/WebServer/Documents/applepay/applepay_includes/ApplePay.crt.pem" and its private key: OSStatus -61"}


I looked up the curl issue of "OSStatus -61" and it led me to https://coderwall.com/p/h3zzrw/using-client-ssl-certificates-for-php-curl-requests-on-osx


In the end, I couldn't get your php server code to make a HTTP post request to apple's web servers (production or sandbox).

Ah, I'd missed that nuance - so you were determining/setting the startSession endpoint url manually/yourself?


No need to do that, as the url is provided when Apple calls your session object’s

onvalidatemerchant
callback function.


While we're in this beta phase of Apple's development of applepayJS they are providing a single/different endpoint url, but when they move out of beta it could be any one of a number of different urls (for load balancing?), but none of which are https://apple-pay-gateway.apple.com/paymentservices/startSession , they're in the format https://apple-pay-gateway-??-????.apple.com/paymentservices/startSession


The sandbox (sandbox != beta ) is entered by virtue of the iphone you use being logged into a sandbox icloud account, it's not determined by the endpoint url used. Sandbox will still be there once ApplePayJS moves out of beta in the Autumn.


I'm testing with both a real and sandbox icloud account and they're both working OK (have notice slight differences, like the sandbox account isn't sending the icloud account's countryCode in the session.onshippingcontactselected callback but it is with a prod icloud account)


ApplePayJS is never going to know if you have a credit card gateway or not. ApplePayJS only exists to give you an encrypted, tokenised credit card number. you can choose to process that yourself or hand it off to a credit card gateway/payment provider (stripe.com, braintreepayments.com) but ApplePayJS has no knowledge of the method you're going to use.

Hey @evermeire1

Similar to what @NorfolkMustard was saying.

Make sure that the URL you're hitting is:

USE THIS ENDPOINT

https://apple-pay-gateway-cert.apple.com/paymentservices/startSession


They have a bug currently that returns the wrong url for our dev requests.

THIS IS THE INCORRECT ENDPOINT, WILL NOT WORK

https://apple-pay-gateway.apple.com/paymentservices/startSession


You're going to have to hardcode it for now because that incorrect URL is being returned in event.url.

I'm getting a valid endoint URL returned currently. Both with live and sandpit iCloud accounts

So, yesterday around 6pm PT, I was able to make a CURL request to the "sandbox/certification" end point of


https://apple-pay-gateway-cert.apple.com/paymentservices/startSession


and I was getting back a JSON response of that contained a "merchantSessionIdentifier" value. Now, as of Aug 12 2:08pm PT, I am getting a HTTP 417 "not registered for service". Hmmm....

I figured out my issue. I actually have to have to the Apple Pay Processing Certificate (This is the one that is created from a ECC/256 certificate signing request). I removed it after I had stuff working and it continued to work (must be some inherit delay, which is to be expected when creating and deleting certificates via the development portal).


To be clear, Apple Pay for the Web will require that Apple Pay certificate(s) exist as well. I am talking about the section with the verbage:


"To configure Apple Pay for this Merchant ID, a Certificate that is used by Apple to encrypt transaction data, is required. Each Merchant ID requires its own Certificate. Manage and generate your certificates below."