Card fund code examples
This page shows how to implement the payload-signing routine described in Payload & Signature. Each example signs a payload with your Card fund provider private key using ES256 over the secp256k1 curve, then base64Url-encodes the result to produce the signature field your response must include.
The three examples below are equivalent implementations in Java, Python, and TypeScript. Pick the one that matches your backend stack; the signing steps are the same in each case.
Each snippet expects a response protobuf message (or an already-encoded payload) and your provider’s private key. Replace the placeholder key path or value with the location of your own key.
Java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.util.Base64;
public class App {
static {
Security.addProvider(new BouncyCastleProvider());
}
public static void main(String[] args) throws Exception {
byte[] payloadBytes = response.toByteArray();
String payload = Base64.getUrlEncoder().withoutPadding().encodeToString(payloadBytes);
PrivateKey key = getPrivateKeyFromPEMFile("<path to your private key>");
byte[] sign = signPayload(("." + payload).getBytes(), key);
String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(sign);
}
public static byte[] signPayload(byte[] payloadBytes, PrivateKey privateKey) throws Exception {
Signature signer = Signature.getInstance("SHA256withPlain-ECDSA", "BC");
signer.initSign(privateKey);
signer.update(payloadBytes);
return signer.sign();
}
}Python
import base64
import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from google.protobuf import message
import sell_pb2 # Import the generated protobuf classes
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
def base64_url_encode(data):
return base64.urlsafe_b64encode(data).rstrip(b'=').decode('utf-8')
def sign_payload(payload, private_key):
# Sign the payload using ECDSA with SHA-256
der_signature = private_key.sign(
payload,
ec.ECDSA(hashes.SHA256())
)
# Decode the DER-encoded signature to get r and s
r, s = decode_dss_signature(der_signature)
# Convert r and s to bytes
r_bytes = r.to_bytes(32, byteorder='big')
s_bytes = s.to_bytes(32, byteorder='big')
return r_bytes + s_bytes
if __name__ == "__main__":
payload = response.SerializeToString()
encoded_payload = base64_url_encode(payload)
# Sign the payload
signature = sign_payload(("." + encoded_payload).encode('utf-8'), private_key)
# Base64 URL encode the signature
encoded_signature = base64_url_encode(signature)
provider_sig = {
"payload": encoded_payload,
"signature": encoded_signature
}typescript
const base64EncodedPayload = base64url.encode(
Buffer.from(uInt8ArrayEncodedPayload), // this is encoded payload using protobuf
);
function signProviderSignaturePayload(base64EncodedPayload: string): string {
const ec = new EC('secp256k1');
const keyPair = ec.keyFromPrivate(
<PRIVATE_KEY_GOES_HERE>,
'hex',
);
const hashedMessage = crypto
.createHash('sha256')
.update(`.${base64EncodedPayload}`)
.digest();
const signature = keyPair.sign(hashedMessage);
const r = signature.r.toString('hex').padStart(64, '0');
const s = signature.s.toString('hex').padStart(64, '0');
const signatureBuffer = Buffer.from(r + s, 'hex');
const signatureBase64 = signatureBuffer.toString('base64url');
return signatureBase64;
}Cross-references
- Backend: the endpoints that consume this signature, and the payload format it signs
- API reference: full request and response schemas for the Card endpoints
Last updated on