post
https://example.com/3ds//accept
Accepts a 3D Secure authentication request with cryptographic signatures.
This endpoint is used to confirm a 3DS authentication after the user has
reviewed the transaction details in their wallet application. The request
must include valid cryptographic signatures proving the user's consent.
Example with Ethersjs:
const wallet = ethers.Wallet.createRandom();
const types = {
Authenticate: [{ name: 'id', type: 'string' }],
};
const domain = {
name: 'Kulipa Wallet Authentication',
version: '1',
chainId: 1, // this is the chain ID of the wallet network
};
const message = {
id: '3ds-e259ee22-f857-444d-9c38-e0bf2ee669e3', // the 3DS authentication ID
};
const signature = await wallet.signTypedData(domain, types, message);
Example with ECDSA signature (when signing from the user wallet is not possible):
Make sure to use the correct private key and passphrase from the public key you shared with Kulipa.
const crypto = require('crypto');
const canonicalize = require('canonicalize');
const payload = {
primaryType: 'Authenticate',
message: {
id: '3ds-e259ee22-f857-444d-9c38-e0bf2ee669e3'
}
};
const canonicalizedPayload = canonicalize(payload);
const sign = crypto.createSign('SHA256');
sign.update(canonicalizedPayload);
sign.end();
const signature = sign.sign({
key: privateKey, // your private key
format: 'pem',
type: 'pkcs8',
passphrase: 'your-passphrase',
}, 'hex');
Example with Solana kit:
const keys = await generateKeyPair();
const address = await getAddressFromPublicKey(keys.publicKey);
const validTypedData = {
cardAuthenticationId: '3ds-e259ee22-f857-444d-9c38-e0bf2ee669e3',
purpose: 'Card 3DS Authentication',
organisation: 'Kulipa',
userId: 'usr-595f5f00-8471-4676-90b0-61a9277e2f78',
nonce: getUnixTime(new Date()), // we validate the nonce is within +/- 5 minutes of the current timestamp to allow for server drift
wallet: address,
};
const structCodec = getStructCodec([
['organisation', addCodecSizePrefix(getUtf8Codec(), getU32Codec())],
['userId', addCodecSizePrefix(getUtf8Codec(), getU32Codec())],
['purpose', addCodecSizePrefix(getUtf8Codec(), getU32Codec())],
['nonce', getU64Codec()],
['wallet', addCodecSizePrefix(getUtf8Codec(), getU32Codec())],
['cardAuthenticationId', addCodecSizePrefix(getUtf8Codec(), getU32Codec())],
]);
const encodedData = structCodec.encode(validTypedData);
const signedBytes = await signBytes(keys.privateKey, encodedData);
const signature = getBase58Decoder().decode(signedBytes);
console.log(signature);
For more details about the 3DS process, see the 3DS documentation.
