node.jssslapplepay

Generate an apple pay paymentSession using node-fetch


I am trying to integrate Apple Pay and as usual the apple documentation is amazing ...

I need to request an Apple Pay Payment Session

The request should be signed with a certificate I have in my Apple developer account

This is my current test file

import express from "express"
import fs from "fs"
import fetch from "node-fetch"
import https from "https"

const app = express()

app.get('/', function (req, res) {
    const html = fs.readFileSync('index.html', 'utf8')
    res.send(html)
})

app.get('/paymentSession', async function(){

    const httpsAgent = new https.Agent({
        rejectUnauthorized: false,
        cert: fs.readFileSync('merchant_id.cer', 'utf8'),
      });

    const response = await fetch('https://apple-pay-gateway.apple.com/paymentservices/paymentSession',{
        method: 'POST',
        headers: {
            'content-type': 'application/json'
        },
        agent: httpsAgent,
        body: JSON.stringify({
            merchantIdentifier: 'merchant.com.xxx.xxx',
            displayName: 'xxx',
            initiative: 'web',
            initiativeContext: 'xxx.com'
        })
    })

    console.log(response.status);

    const text = await response.text()

    console.log(text);

    res.json({})
})

app.listen(3000)

I am trying with the certificate downloaded from my Apple developer account merchant_id.cer, and I have the folowing error

node:internal/tls/secure-context:70
    context.setCert(cert);
            ^

Error: error:0480006C:PEM routines::no start line
    at node:internal/tls/secure-context:70:13
    at Array.forEach (<anonymous>)
    at setCerts (node:internal/tls/secure-context:68:3)
    at configSecureContext (node:internal/tls/secure-context:157:5)
    at Object.createSecureContext (node:_tls_common:116:3)
    at Object.connect (node:_tls_wrap:1718:48)
    at Agent.createConnection (node:https:169:22)
    at Agent.createSocket (node:_http_agent:342:26)
    at Agent.addRequest (node:_http_agent:289:10)
    at new ClientRequest (node:_http_client:337:16) {
  library: 'PEM routines',
  reason: 'no start line',
  code: 'ERR_OSSL_PEM_NO_START_LINE'
}

Then trying to convert merchant_id.cer to merchant_id.pem using openssl x509 -inform der -in merchant_id.cer -out merchant_id.pem I have

file:///Users/ajouve/Developer/test/apple-pay/node_modules/node-fetch/src/index.js:108
            reject(new FetchError(`request to ${request.url} failed, reason: ${error.message}`, 'system', error));
                   ^

FetchError: request to https://apple-pay-gateway.apple.com/paymentservices/paymentSession failed, reason: 80E06BF801000000:error:0A00045C:SSL routines:ssl3_read_bytes:tlsv13 alert certificate required:ssl/record/rec_layer_s3.c:1586:SSL alert number 116

    at ClientRequest.<anonymous> (file:///Users/ajouve/Developer/test/apple-pay/node_modules/node-fetch/src/index.js:108:11)
    at ClientRequest.emit (node:events:526:35)
    at TLSSocket.socketErrorListener (node:_http_client:495:9)
    at TLSSocket.emit (node:events:514:28)
    at TLSSocket._emitTLSError (node:_tls_wrap:994:10)
    at TLSWrap.onerror (node:_tls_wrap:482:11) {
  type: 'system',
  errno: 'ERR_SSL_TLSV13_ALERT_CERTIFICATE_REQUIRED',
  code: 'ERR_SSL_TLSV13_ALERT_CERTIFICATE_REQUIRED',
  erroredSysCall: undefined
}

Seems that the certificate is not valid


Solution

  • Found the issue, I had to generate a csr with private key and then use it

    Generate the csr

    openssl req -newkey rsa:2048 -keyout merchant_id.key -out merchant_id.csr
    

    Convert the cer to pem

    openssl x509 -inform der -in merchant_id.cer -out merchant_id.pem
    

    Configure the httpsAgent

    const httpsAgent = new https.Agent({
      rejectUnauthorized: false,
      cert: fs.readFileSync('cert/merchant_id.pem', 'utf-8'),
      key: fs.readFileSync('cert/merchant_id.key', 'utf-8'),
      passphrase: 'xxx'
    });