Card Acquiring and Transfers

In the MBanq Cloud API, acquiring cards refers to the process of linking cards from other institutions to an end user's account. This functionality allows users to conveniently manage their funds across multiple financial institutions within the MBanq Cloud ecosystem. With acquiring cards, users can perform push or pull transfers to and from external accounts using the card network, enabling fast and secure fund transfers.

Available Funds Transfer Methods

There are multiple transfer channels to facilitate moving money into and out of your customer accounts.

Transfer MechanismSpeedCostBank CoverageOperatingMax TransferFunds Guaranteed
Card AcquiringFastModerateEvery BankEvery Day$1,000Mostly
ACH2 DayLowEvery BankBusiness Days$1,000,000No
Same Day ACHSame DayModerateEvery BankBusiness Day$1,000,000No
WireSame DayExpensiveMost BanksBusiness DaysUnlimitedYes
Real Time Payment (RTP)FastModerateSome BanksEvery Day$1,000,000Yes

Card acquiring transfers offer a good balance of speed and cost.

📘

API References:

Flow of External Cards Usage

Using external cards in MBanq Cloud involves the following flow:

  1. Linking the Card: Users can link cards from other institutions to their MBanq Cloud account.

  2. Push Transfers: Users can perform push transfers, where funds are transferred from their MBanq Cloud account to the linked external account. This allows users to conveniently push funds to cover expenses or make payments using their card.

  3. Pull Transfers: Users can initiate pull transfers, where funds are transferred from an external account to their MBanq Cloud account. This facilitates easy consolidation and management of funds.

To comply with the Payment Card Industry Data Security Standard (PCI DSS), which ensures the security of cardholder data, Mbanq aims to minimize the FinTech's PCI compliance requirements. One way to achieve this is by not storing sensitive card data, such as the card number, expiry date, and CVV code. Instead, all sensitive data will be encrypted with a public key on the user's device at the time of card entry. The encrypted data will then be securely transmitted to Mbanq, where it will be tokenized. A unique token will be issued to identify the acquired card, which will be used for all subsequent activities on the cards.

Encryption Standards and Requirements

To ensure strong encryption, Mbanq adopts the RSA encryption algorithm with the transformation of RSA/ECB/OAEPWithSHA-256AndMGF1Padding. This ensures the secure encryption of sensitive card data before transmission. The following requirements are important for implementing encryption:

  1. RSA Encryption with OAEP and SHA-256: Use the RSA algorithm with Optimal Asymmetric Encryption Padding (OAEP) and SHA-256 hash for encryption.
  2. Public Key: Obtain the public key required for encryption using the API : Public Key API . The public key is used to encrypt the sensitive card data.
  3. Base64 URL-Safe Encoding: Convert the encrypted data to Base64 URL-Safe format before transmitting it to Mbanq's servers.

Example: Card Encryption in Java

package com.mbanq;

import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class Encryption {
	private static final String msf_sRSA_ALGORITHM = "RSA";
	private static final String msf_sRSA_TRANSFORMATION = "RSA/ECB/OAEPPadding";
	private static final Base64.Encoder msf_encoderBase64 = Base64.getUrlEncoder().withoutPadding();
	private static final Base64.Decoder msf_decoderBase64 = Base64.getUrlDecoder();
	public static void main(String[] args) throws Exception {
		final var result = encryptUsingPublicKey("Public Key",
		                                         "Card Number|Expired|CVV");
		System.out.println("Encrypted : " + result);
	}

	public static String encryptUsingPublicKey(String p_sEncodedPublicKey, String p_sData) throws Exception {
		if (p_sEncodedPublicKey != null && p_sEncodedPublicKey.length() != 0) {
			if (p_sData != null && p_sData.length() != 0) {
				byte[] abPublicKey;
				try {
					abPublicKey = msf_decoderBase64.decode(p_sEncodedPublicKey);
				} catch (Throwable var10) {
					throw new Exception("Data", var10);
				}

				try {
					KeyFactory factoryKey = KeyFactory.getInstance("RSA");
					X509EncodedKeySpec specX509EncodedKey = new X509EncodedKeySpec(abPublicKey);
					PublicKey keyPublic = factoryKey.generatePublic(specX509EncodedKey);
					Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
					OAEPParameterSpec paramSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
					cipher.init(1, keyPublic, paramSpec);
					byte[] abData = cipher.doFinal(p_sData.getBytes(StandardCharsets.UTF_8));
					return msf_encoderBase64.encodeToString(abData);
				} catch (Throwable var9) {
					throw new Exception("Encrypt Using Public Key", var9);
				}
			} else {
				throw new Exception("Data");
			}
		} else {
			throw new Exception("Public Key");
		}
	}
}
/**
 * @param {string} str
 */
function unescapeBase64(str) {
  // convert Base64 URL-Safe to Base64 regular
  return (str + '==='.slice((str.length + 3) % 4)).replace(/-/g, '+').replace(/_/g, '/');
}

/**
 * @param {string} str
 */
function escapeBase64(str) {
  // convert Base64 regular to Base64 URL-Safe
  return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}

/**
 * @param {string} keyID
 */
function importRSAKey(keyID) {
  // fetch the part of the PEM string between header and footer
  const pemContents = unescapeBase64(keyID)
  // base64 decode the string to get the binary data
  const binaryDerString = window.atob(pemContents);
  // convert from a binary string to an ArrayBuffer
  const binaryDer = stringToArrayBuffer(binaryDerString);

  return window.crypto.subtle.importKey("spki", binaryDer, { name: "RSA-OAEP", hash: "SHA-256" }, true, ["encrypt"]);
}

/**
 * @param {string} str
 */
function stringToArrayBuffer(str) {
  // String to array buffer
  const buf = new ArrayBuffer(str.length);
  const bufView = new Uint8Array(buf);

  for (let i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }

  return buf;
}

/**
 * @param {string} value
 */
function getStringEncoding(value) {
  let enc = new TextEncoder();
  return enc.encode(value);
}

/**
 * @param {ArrayBuffer} buffer
 */
function arrayBufferToBase64(buffer) {
  let binary = '';
  const bytes = new Uint8Array(buffer);
  const len = bytes.byteLength;
  for (let i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return window.btoa(binary);
}

(function () {
  // keyID is ASN.1 Format Base64 URL-Safe
  const keyID = 'WcCBIjAnBGkhki...';

  // cardInfo format `${cardAccountNumber}|${cardExpirationDate format('YYYYMM')}|${cardSecurityCode}`
  const cardInfo = '9010100999999995|209912|123';

  importRSAKey(keyID).then(RSAKey => {
    let encoded = getStringEncoding(cardInfo);

    window.crypto.subtle.encrypt({ name: "RSA-OAEP" }, RSAKey, encoded)
      .then(data => {
        // encrypted data
        const dataEncrypted = arrayBufferToBase64(data);

        // use value return from escapeBase64(dataEncrypted) with Tabapay
        console.log(escapeBase64(dataEncrypted))
      });
  });
})();

In the provided code snippets, you can see an example implementation of encrypting card information using a public key. The code demonstrates how to perform encryption using RSA/ECB/OAEPPadding transformation. You can adapt this code to your specific implementation needs, ensuring that you handle the encryption and decryption processes securely.

Additional Considerations

  • Test Card Numbers: When testing the implementation, make sure to use test card numbers provided below. Never use real card numbers in the Sandbox Environment to comply with PCI requirements. The following Card Numbers were randomly generated, and any resemblance to actual Card Numbers is purely coincidental.
NetworkCard NumberRegulatedCard Type- DebitCard Type - CreditCard Type- PrePaidPullPush (Availability) - ImmediatePush (Availability) - NextPush (Availability) - Few
Visa4000056655665556✘ No
4005519200000004✔ Yes
4111111111111111✔ Yes
4012000077777777✔ Yes
4000000760000002✔ Yes
4000001240000000✔ Yes
4000004840008001✔ Yes
4500600000000061✘ No
4217651111111119✘ No
4242424242424242✘ No
Mastercard2223000048400011✘ No
5200828282828210✔ Yes
5403879999999997✔ Yes
5105105105105100✔ Yes
MoneySend2223003122003222✘ No
5555555555554444✔ Yes
American Express371449635398431✔ Yes
378282246310005✔ Yes
378734493671000✔ Yes
Discover6011111111111117✔ Yes
6011000990139424✔ Yes
6011000991300009✔ Yes

  • Security Measures: Follow best practices for authentication and security measures to protect user data and ensure secure transactions.

For more detailed information on using external cards in MBanq Cloud API, refer to the tutorial in the next section.