0
votes

I can't seem to be able to get the merchant session validation working with Ruby. Tried HTTParty and RestClient and I'm getting:

OpenSSL::SSL::SSLError (SSL_connect returned=1 errno=0 state=SSLv3 read finished A: sslv3 alert certificate expired):

I tried the same certificate with this node server example, https://github.com/tomdale/apple-pay-merchant-session-server, and it worked fine, so it must be something in my ruby code.

Has anyone managed to get this working?

1

1 Answers

4
votes

I was having the same problem. With the help of the example you referenced and the implementation at https://github.com/norfolkmustard/ApplePayJS (see also the discussion about the implementation at https://forums.developer.apple.com/thread/51580) I was able to get it working.

The key for me was passing in the correct certificate (the Apple Pay Merchant Identity certificate) just as Apple provides it and getting the cert key like so:

Once you have your Merchant ID (session) certificate from Apple, import that into keychain.app on your Mac by double-clicking it, right click on the cert in keychain and export the combined private-key and cert as a .p12 file then, in terminal:-

openssl pkcs12 -in your_merchant_identity_cert_name.p12 -out ApplePay.key.pem -nocerts -nodes

After adding the Apple Pay Merchant Identification cert from Apple and the contents of the ApplePay.key.pem file to an environment variable I was able to construct the following request using Ruby's Net::HTTP class...

class YourControllerName < ApplicationController

  def apple_pay_validation
    respond_to do |format|
      format.json { render json: start_apple_session(params[:url]) } if params[:url].include?('apple.com')
    end
  end

  private

  def start_apple_session(url)
    uri = URI.parse(url) # the url from event.validationURL
    data = {'merchantIdentifier' => "merchant.com.your_site_name", 'domainName' => "your_doamin", 'displayName' => "your_company_name"}
    pem = File.read('path/to/your/merchant_id.cer')
    key = ENV['APPLE_PAY_MERCHANT_ID_ KEY']
    passphrase = 'passphrase set up when exporting certificate in keychain' # Should be an environment variable
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    http.ssl_version = :TLSv1_2
    http.ciphers = ['ECDHE-RSA-AES128-GCM-SHA256']
    http.cert = OpenSSL::X509::Certificate.new(pem)
    http.key = OpenSSL::PKey::RSA.new(key, passphrase)
    http.verify_mode = OpenSSL::SSL::VERIFY_PEER
    request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
    request.body = data.to_json
    response = http.request(request)
    response.body
  end

end

This was called from my performValidation function (modified from the ApplePayJS repo listed above) which looks like this..

performValidation = (valURL) ->
  new Promise((resolve, reject) ->
    xhr = new XMLHttpRequest
    xhr.open 'GET', '/your_controller_name/apple_pay_validation?url=' + valURL
    xhr.onerror = reject
    xhr.onload = ->
      data = JSON.parse(@responseText)
      resolve data
    xhr.send()
  )

Hopefully that helps save someone some time and gray hairs!