Integration test

Description

If integration for the Create transaction API and Web Checkout SDK is done well, you are ready to test the Web Checkout service and go to the next step. For the next step and integration testing, you can refer to below screen shot.

At step #6, you need to retrieve ref_id from the query parameter and call the Get payment credentials API for getting payment credential data. After getting it, you can process your own payment flow. Finally, if you get the payment result from the Acquirer, deliver it by calling the Notify payment result API.
.
Payment Flow

Appendix A. Decrypting payment credentials data

A.1. Generate der file using your private key

$ openssl pkcs8 -topk8 -in Merchant.key -outform DER -nocrypt -out rsapriv.der

A.2. Decrypt JWE credential data

Sample code(Java)'

import java.io.*;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import javax.crypto.*;
...
public static void main(String[] args) {
  String encPayload = {{encryptedPayload}};
  String privateKeyFilePath = "./rsapriv.der";
  getDecryptedData(encPayload, privateKeyFilePath);
}

public static String getDecryptedData(String encPayload, String privateKeyFilePath) {
  String delims = "[.]";
  String[] tokens = encPayload.split(delims);

  Decoder urlDecoder = Base64.getUrlDecoder();
  byte[] encKey = urlDecoder.decode(tokens[1]);
  byte[] iv = urlDecoder.decode(tokens[2]);
  byte[] cipherText = urlDecoder.decode(tokens[3]);
  byte[] tag = urlDecoder.decode(tokens[4]);
  byte[] plainText = new byte[cipherText.length];

  try {
    // Read private key file
    File privateKeyFile = new File(privateKeyFilePath);
    DataInputStream dis = new DataInputStream(new FileInputStream(privateKeyFile));
    byte[] privKeyBytes = new byte[(int) privateKeyFile.length()];
    dis.read(privKeyBytes);
    dis.close();

    // Set private key spec
    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privKeyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PrivateKey privKey = keyFactory.generatePrivate(spec);

    Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    decryptCipher.init(Cipher.DECRYPT_MODE, privKey);
    byte[] plainEncKey = decryptCipher.doFinal(encKey);

    final Cipher aes128Cipher = Cipher.getInstance("AES/GCM/NoPadding");
    final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * Byte.SIZE, iv);
    final SecretKeySpec keySpec = new SecretKeySpec(plainEncKey, "AES");
    aes128Cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);

    int offset = aes128Cipher.update(cipherText, 0, cipherText.length, plainText, 0);
    aes128Cipher.update(tag, 0, tag.length, plainText, offset);
    aes128Cipher.doFinal(plainText, offset);
  } catch (Exception e) {
  }
  return new String(plainText);
}

Sample code(C#)

  • The following packages need to be installed for C# .netCore
Install-Package ChilkatDnCore -Version 9.5.0.69 (https://www.nuget.org/packages/ChilkatDnCore/)
Install-Package jose-jwt (https://www.nuget.org/packages/jose-jwt/)
using Jose;
using System;
using System.Text;
...
static void Main(string[] args)
{
  String encPayload = {{encryptedPayload}};
  String privateKeyFilePath = "./rsapriv.der";
  getDecryptedData(encPayload, privateKeyFilePath);
}

static void getDecryptedData(String encPayload, String privateKeyFilePath) {
  char delims = '.';
  string[] tokens = encPayload.Split(delims);
  byte[] header = Base64Url.Decode(tokens[0]);
  string headerString = Encoding.UTF8.GetString(header);
  Console.WriteLine("headerString: " + headerString);
  byte[] encKey = Base64Url.Decode(tokens[1]);
  byte[] iv = Base64Url.Decode(tokens[2]);
  byte[] cipherText = Base64Url.Decode(tokens[3]);
  byte[] tag = Base64Url.Decode(tokens[4]);
  byte[] plainText = new byte[cipherText.Length];

  Chilkat.Rsa rsa = new Chilkat.Rsa();
  bool success = rsa.UnlockComponent("Anything for 30-day trial");
  if (success != true) {
    Console.WriteLine(rsa.LastErrorText);
    return;
  }

  Chilkat.PrivateKey privateKey = new Chilkat.PrivateKey();
  success = privateKey.LoadPemFile(privateKeyFilePath);
  if (success != true) {
    Console.WriteLine(privateKey.LastErrorText);
    return;
  }

  success = rsa.ImportPrivateKeyObj(privateKey);
  if (success != true) {
    Console.WriteLine(rsa.LastErrorText);
    return;
  }

  rsa.EncodingMode = "base64";
  rsa.OaepPadding = false;
  bool usePrivateKey = true;
  byte[] plainEncKey = rsa.DecryptBytes(encKey, usePrivateKey);
  Console.WriteLine(
      "plainEncKey: " + Encoding.UTF8.GetString(plainEncKey) + ", size: " + plainEncKey.Length);

  AesGcmEncryption aesGcmEngine = new AesGcmEncryption(128);
  plainText = aesGcmEngine.Decrypt(null, plainEncKey, iv, cipherText, tag);
  Console.WriteLine("plainText: " + Encoding.UTF8.GetString(plainText));
}

Appendix B. Configure the Health Check API

Sample code(Java)

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.web.bind.*;
import org.springframework.web.client.*;

@RestController
public class HealthCheckController {

    @Value("${ops.address}")
    private String onlinePaymentsAddress;

    @RequestMapping(method = RequestMethod.HEAD, value = "/samsungpay/integration/health")
    public ResponseEntity<Void> health() {

        String healthcheckAddress = onlinePaymentsAddress + "/ops/v1/internal/health";
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<Void> healthcheckResponse;

        try {
            healthcheckResponse = restTemplate.exchange(healthcheckAddress, HttpMethod.HEAD,
                    null, Void.class);
        } catch (RestClientException e) {
            return new ResponseEntity<>(HttpStatus.BAD_GATEWAY);
        }

        if (healthcheckResponse.getStatusCode() != HttpStatus.NO_CONTENT) {
            return new ResponseEntity<>(HttpStatus.BAD_GATEWAY);
        }

        return healthcheckResponse;
    }
}