HandCash provides webhook notifications for all activities related to your application’s items, allowing you to stay synchronized with blockchain events that occur outside your game or application.
Webhook Types
There are three types of webhooks available:
- Item Listing Payment Completed
- Items Transferred
- Item Creation Order Completed
Configure these webhooks in the Developer Dashboard.
Webhook Authentication
The signature is passed in the handcash-signature
header of the webhook request.
To ensure the authenticity of webhook calls, implement the following authentication process:
import crypto from 'crypto';
function getWebhookEvent(signature: string, body: any, appSecret: string): WebhookPayload {
if (!signature) {
throw new Error('No signature provided');
}
const fiveMinutesAgo = new Date(new Date().getTime() - 5 * 60000);
if (new Date(body.created) < fiveMinutesAgo) {
throw new Error('Timestamp is too old');
}
const hmac = crypto.createHmac('sha256', appSecret);
hmac.update(JSON.stringify(body));
const generatedSignature = hmac.digest('hex');
if (generatedSignature !== signature) {
throw new Error('Invalid signature');
}
return body as WebhookPayload;
}
Webhook Payload Structure
All webhooks share a common base structure:
interface WebhookPayload {
event: 'item_listing_payment_completed' | 'items_transferred' | 'item_creation_order_completed';
applicationId: string;
apiVersion: string;
created: string;
}
Item Listing Payment Completed
Triggered when a payment for a listed item is completed.
interface ItemListingPaymentCompletedEventPayload extends WebhookPayload {
event: 'item_listing_payment_completed';
data: {
itemTransfer: ItemTransfer;
};
}
Items Transferred
Triggered when items are transferred between users.
interface ItemsTransferredEventPayload extends WebhookPayload {
event: 'items_transferred';
data: {
itemTransfer: ItemTransfer;
};
}
Item Creation Order Completed
Triggered when an item creation order is completed.
interface ItemCreationEventPayload extends WebhookPayload {
event: 'item_creation_order_completed';
data: {
items: Item[];
itemCreationOrder: CreateItemsOrder;
};
}
Key Types
ItemTransfer
type ItemTransfer = {
id: string;
label: 'send' | 'receive' | 'marketBuy' | 'marketSell' | 'marketCancel';
payment?: ItemPayment;
items: SingleItemTransfer[];
createdAt: string;
};
Item
type Item = {
id: string;
origin: string;
name: string;
description: string;
imageUrl: string;
multimediaUrl: string;
multimediaType: string;
rarity: string;
color: string;
attributes: ItemAttribute[];
externalId?: string;
collection: {
id: string;
description: string;
app: {
id: string;
name: string;
iconUrl: string;
};
origin: string;
name: string;
imageUrl: string;
};
user: {
alias: string;
displayName: string;
profilePictureUrl: string;
};
app: {
id: string;
name: string;
iconUrl: string;
};
itemListing: {
id: string;
status: string;
currencyCode: string;
price: number;
denominatedIn: string;
paymentRequestUrl: string;
paymentRequestId: string;
fiatEquivalent: {
currencyCode: string;
amount: number;
};
};
};
CreateItemsOrder
type CreateItemsOrder = {
id: string;
type: 'collectionItem' | 'collection';
status: 'preparing' | 'pendingPayment' | 'pendingInscriptions' | 'completed';
collectionOrdinalId?: string;
items: CreateItemMetadata[];
payment?: {
paymentRequestId: string;
paymentRequestUrl: string;
amountInUSD: number;
transactionId: string;
isConfirmed: boolean;
};
pendingInscriptions?: number;
error?: string;
uid?: string;
};
Handling Webhooks
- Set up an endpoint in your application to receive webhook POST requests.
- Verify the webhook signature using the authentication process above.
- Parse the webhook payload based on the
event
type.
- Update your application’s state or trigger relevant actions based on the webhook data.
Example webhook handler:
app.post('/webhooks/handcash-items', (req, res) => {
try {
const signature = req.headers['handcash-signature'] as string;
const event = getWebhookEvent(signature, req.body, process.env.APP_SECRET);
switch (event.event) {
case 'item_listing_payment_completed':
handleItemSold(event as ItemListingPaymentCompletedEventPayload);
break;
case 'items_transferred':
handleItemTransfer(event as ItemsTransferredEventPayload);
break;
case 'item_creation_order_completed':
handleItemCreation(event as ItemCreationEventPayload);
break;
}
res.sendStatus(200);
} catch (error) {
console.error('Webhook error:', error);
res.sendStatus(400);
}
});
Best Practices
- Always verify the webhook signature to ensure the request is from HandCash.
- Use the
handcash-signature
header to retrieve the signature for verification.
- Implement idempotency to handle potential duplicate webhook deliveries.
- Process webhooks asynchronously to avoid blocking your application.
- Store raw webhook data for debugging and auditing purposes.
- Implement proper error handling and logging for webhook processing.
Signature Generation
For reference, HandCash generates the signature using the following method:
static getAppSecretSignature(appSecret: string, body: any): string {
const hmac = crypto.createHmac('sha256', appSecret);
hmac.update(JSON.stringify(body));
return hmac.digest('hex');
}
By leveraging these webhooks, your application can stay up-to-date with all item-related activities, ensuring a seamless experience for your users across the HandCash ecosystem and your application.