rubyapplepay

Apple Pay on the web merchant session in ruby


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?


Solution

  • 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!