Physical Cards
Learn more about physical cards.
Encrypted PIN Block (Enterprise)
Physical cards can be provisioned for ATM/PIN debit access which requires a cardholder PIN. The Create card and Update card endpoints can set and update the cardholder PIN.
Due to the sensitive nature, PINs must be encrypted on the frontend immediately after user input.
An Encrypted PIN block is a JSON blob, encrypted with the Lithic API public key, base64 digest.
{
"nonce": Integer,
"pin": String
}
nonce | A cryptographic nonce to create additional entropy and prevent replay attacks. This should be unique per request. Integer should be at least 8 digits in length. |
pin | Cardholder PIN (between 4 and 12 numeric digits in length). String datatype to ensure leading zeros are not truncated. |
Examples
import base64
import json
import random
# pip install pycryptodome
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
pin_block = {
"nonce": random.randint(1e8, 1e12),
"pin": "1234"
}
with open("path/to/api.lithic.com.pub.pem") as f:
cipher = PKCS1_OAEP.new(RSA.importKey(f.read()))
ciphertext = cipher.encrypt(json.dumps(pin_block).encode('utf-8'))
# Use this for the "pin" field value when creating or updating cards
encrypted_pin_block = base64.b64encode(ciphertext)
const fs = require("fs");
// npm install node-forge
const forge = require("node-forge");
const pem = fs.readFileSync("path/to/api.lithic.com.pub.pem", "utf8");
const publicKey = forge.pki.publicKeyFromPem(pem);
function randomInt(low, high) {
// Generate random integer between low and high, inclusive
return Math.floor(Math.random() * (high - low + 1) + low)
}
const pinBlock = {
"nonce": randomInt(1e8, 1e12),
"pin": "1234"
}
const ciphertext = publicKey.encrypt(JSON.stringify(pinBlock), "RSA-OAEP", {
md: forge.md.sha1.create(),
mgf1: {
md: forge.md.sha1.create()
}
});
// Use this for the "pin" field value when creating or updating cards
const encryptedPinBlock = forge.util.encode64(ciphertext);
console.log(encryptedPinBlock);
package main
import (
crand "crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
rand "math/rand"
"os"
"time"
)
type PinBlock struct {
Nonce int `json:"nonce"`
Pin string `json:"pin"`
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
func loadKey(fileName string) (*rsa.PublicKey, error) {
rawPem, err := ioutil.ReadFile(fileName)
checkError(err)
pemBlock, _ := pem.Decode(rawPem)
return x509.ParsePKCS1PublicKey(pemBlock.Bytes)
}
func encrypt(msg []byte, publicKey *rsa.PublicKey) (string, error) {
encryptedBytes, err := rsa.EncryptOAEP(
sha1.New(),
crand.Reader,
publicKey,
msg,
nil,
)
if err != nil {
return "", err
}
encodedData := base64.StdEncoding.EncodeToString(encryptedBytes)
return encodedData, nil
}
func generateNonce() int {
rand.Seed(time.Now().UnixNano())
min := int(1e8)
max := int(1e12)
return min + rand.Intn(max-min+1)
}
func main() {
pemFile := "path/to/api.lithic.com.pub.pem"
publicKey, err := loadKey(pemFile)
pinBlock := &PinBlock{Nonce: generateNonce(), Pin: "1234"}
pinBlockBytes, err := json.Marshal(pinBlock)
checkError(err)
fmt.Println(string(pinBlockBytes))
// Use this for the "pin" field value when creating or updating cards
encoded, err := encrypt(pinBlockBytes, publicKey)
checkError(err)
fmt.Println(encoded)
}
Reissue Card (Enterprise)
API reference: Reissue Card
Initiate print and shipment of a duplicate card. Only applies to cards of type PHYSICAL
.
POST https://api.lithic.com/v1/card/reissue
Sample Request
curl https://api.lithic.com/v1/card/reissue \
-X POST \
-H "Authorization: api-key YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"card_token":"589e1412-b132-48ac-ab5c-befd02535a57"}'
Sample Response
{
"created": "2021-06-28T22:58:36Z",
"cvv": "162",
"exp_month": "06",
"exp_year": "2027",
"funding": {
"account_name": "Sandbox",
"created": "2020-07-08 17:57:36",
"last_four": "5263",
"nickname": "",
"state": "ENABLED",
"token": "b0f0d91a-3697-46d8-85f3-20f0a585cbea",
"type": "DEPOSITORY_CHECKING"
},
"hostname": "",
"last_four": "5205",
"memo": "",
"pan": "4111111540285205",
"spend_limit": 0,
"spend_limit_duration": "TRANSACTION",
"state": "PENDING_FULFILLMENT",
"token": "589e1412-b132-48ac-ab5c-befd02535a57",
"type": "PHYSICAL"
}
card_token | The unique token of the card to update |
shipping_address (optional) | Shipping address. If omitted, the previous shipping address will be used |
product_id (optional) | Alphanumeric identifier to specify physical card manufacturing attributes. Only applies to cards of type PHYSICAL . This must be configured with Lithic before use. |
Updated 8 months ago