π Webhook notifications
This section covers server-to-server communication that happens in case of some events
General information
If you are interested in receiving webhook notifications, please provide your webhook endpoint URL to [email protected].
As a user of the TGB API, you can subscribe to some events that happen during the donation process. TGB has implemented event notifications as HTTP Webhooks.
Security Measures:
- payload field is AES encrypted
- all eventsβ payloads will contain eventTimestamp field, so consumer may implement timestamp check, i.e. to not accept if message is older than 1 hour and/or emit an security alert in this case
Below you can find information about different types of webhooks TGB sends to subscribers.
Event: Deposit completed
When the deposit transaction is completed, The Giving Block will detect that and this notification will be sent as HTTP POST request to your notifications URL if enabled for your API user.
POST Body example:
Note: that βpayloadβ will be passed to you in encrypted way. POST Body of the webhook will look like
{ "eventType": "DEPOSIT_TRANSACTION", "payload": "hex-encrypted-string" }
{
"eventType": "DEPOSIT_TRANSACTION",
"payload": {
"type": "Deposit",
"id": "8f2191c1-d28c-40ca-83b0-481828cbd8d0",
"status": "Advanced | Complete",
"timestampms": "1507913541275", // The time that the trade was executed in milliseconds
"eid": "309356152", // Transfer event id
"transactionHash": "0x12344567898909123124125125abcdefabcdefabcdef",
"currency": "ETH",
"amount": 36.00,
"organizationId": 1234567,
"eventTimestamp": "15079132214598",
"pledgeId": "fe497575-cf4b-4eec-b7e3-b6ce75298bdb",
"valueAtDonationTimeUSD": 45000,
"paymentMethod": "Crypto",
"payoutAmount": null,
"payoutCurrency": "USD",
"externalId": "my-transaction-id",
"grossAmountInPayoutCurrency": null,
}
}
Explanation on some notification fields
- pledgeId is an unique identifier of a pledge. You can use this id to match the donation address you received on CreateDepositAddress API call
- valueAtDonationTimeUSD is a USD equivalent of transaction's amount at the time of donation. Please note that this amount is before any fees are withdrawn and should not be used for any calculation. This is primarily used for nonprofits who are HODLing and not immediately converting. This means that organizations can get approximate donation amount, in case autoconversion is disabled for the organization.
- paymentMethod - Enum (Crypto,Card,Stock)
- transactionHash is transaction hash or ID on the blockchain
- payoutAmount is an amount in currency returned as payoutCurrency that the nonprofit will receive as their final balance (gross donation amount - total fees). For crypto donations the amount will be provided once transaction is converted.
- payoutCurrency the currency for payoutAmount.
- externalId the nonprofit ID (optional) that was passed as a parameter while retrieving Widget URL (see "Widget URL" API reference for more details).
Event: Transaction converted
This event is being sent only for crypto donations.
When the transaction is converted, The Giving Block will detect that and this notification will be sent as HTTP POST request to your notifications URL if enabled for your API user.
POST Body example:
Note: βpayloadβ will be passed to you in encrypted way. POST Body of the webhook will look like
{ "eventType": "TRANSACTION_CONVERTED", "payload": "hex-encrypted-string" }
{
"eventType": "TRANSACTION_CONVERTED",
"payload": {
"type": "Deposit",
"id": "8f2191c1-d28c-40ca-83b0-481828cbd8d0",
"status": "Advanced | Complete",
"timestampms": "1507913541275", // The time that the trade was executed in milliseconds
"eid": "309356152", // Transfer event id
"transactionHash": "0x12344567898909123124125125abcdefabcdefabcdef",
"currency": "BTC",
"amount": 1.00,
"organizationId": 1234567,
"eventTimestamp": 15079132214598,
"convertedAt": "16079132214598",
"netValueAmount": 49000.00,
"grossAmount": 50000,
"netValueCurrency": "USD",
"pledgeId": "fe497575-cf4b-4eec-b7e3-b6ce75298bdb",
"valueAtDonationTimeUSD": 50000,
"pledgeId": "fe497575-cf4b-4eec-b7e3-b6ce75298bdb",
"payoutAmount": 49000.00,
"payoutCurrency": "USD",
"externalId": "my-transaction-id",
"grossAmountInPayoutCurrency": 50000.00
}
}
Explanation on some fields
- netValueAmount is an amount in currency returned as netValueCurrency that the nonprofit will receive as their final balance (gross donation amount - total fees).
- grossAmount is an amount that is calculated during conversion of donated crypto.
- valueAtDonationTimeUSD is a USD equivalent of transaction's amount at the time of donation. Please note that this amount is before any fees are withdrawn and should not be used for any calculation. This is primarily used for nonprofits who are HODLing and not immediately converting. This means that organizations can get approximate donation amount, in case autoconversion is disabled for the organization.
- payoutAmount is an amount in currency returned as payoutCurrency that the nonprofit will receive as their final balance (gross donation amount - total fees).
- payoutCurrency the currency for payoutAmount.
- externalId the nonprofit ID (optional) that was specified as a parameter while retrieving Widget URL (see "Widget URL" API reference for more details).
Example:
A donor made a donation of 1 BTC, when the BTCUSD exchange rate is 50,000 USD.
- The donation amount equals 1 BTC
- valueAtDonationTimeUSD is 50,000 USD (amount * BTCUSD rate)
- For example, TGB converts the donation with average exchange rate of 50,000 USD per 1 BTC.
- Before the conversion happens, all fees are withdrawn. In this example, there is a 2% fee applied so the amount to convert to USD is 0.98 BTC (1 BTC - total fees).
- Therefore, netValueAmount will be calculated by taking 0.98 BTC * 50,000 USD = 49,000 USD
- So at the end of donation processing, the nonprofit organization will have 49,000 USD as their balance which will then be transferred to their bank account.
Event: Merchant status events
After onboarding a merchant to receive credit card donations, all the onboarding data will be reviewed by underwriting team. Any merchant status update will be sent as a HTTP POST request to your notification URL if enabled for your API user.
POST body example:
Note: that βpayloadβ will be passed to you in encrypted way. POST Body of the webhook will look like
{ "eventType": "DEPOSIT_TRANSACTION", "payload": "hex-encrypted-string" }
{
"eventType": "MERCHANT_STATUS_EVENT",
"payload": {
"timestamp": 1709063291321,
"eventType": "MERCHANT_STATUS_EVENT",
"mid": "11223344556677",
"status": "600",
"statusChangeReason": "Reviewed.",
"officeId": "S4TGB"
}
}
Explanation of fields:
- timestamp is time of the event in milliseconds
- eventType is ebent type (will always be MERCHANT_STATUS_EVENT)
- mid is Merchant ID
- status is merchant status
- statusChangeReason is brief description
- officeId is Office ID
Possible merchant statuses:
Status # | Status name |
---|---|
125 | RESUBMITTED APPLICATION |
200 | DECLINED |
300 | CLOSED - MERCHANT CANCELLED |
325 | CLOSED - TERMINATION |
330 | SEASONAL BUSINESS |
350 | GATEWAY / ACCESSORY SERVICE ONLY |
375 | AWAITING ISO ACTIVATION OF UNDERWRITING |
390 | ORIG. APP RECEIVED - IN UNDERWRITING |
400 | IN UNDERWRITING |
425 | ON PRIMARY HOLD - NEED ADDITIONAL INFORMATION |
500 | APPROVED - IN DATA ENTRY FOR MID BUILD |
550 | APPROVED - AWAITING FAXED DOCUMENTATION |
600 | APPROVED |
650 | APPROVED - INSTALLED |
675 | MERCHANT REQUESTING TO CANCEL |
700 | APPROVED - PROCESSING |
730 | APPROVED - SEASONAL HOLD |
785 | LEGAL REVIEW |
Payload decryption
To decrypt the payload you need a pair of "encryption key" and "iv" shared with you during your API credentials creation.
You can find some code snippets below. Given:
encryption key: ae97bfa25b4abcbb7d8722fc5c60747bf7687ec22d1463174f0176ad6daa96ec
encryption iv: 9663f5821170b172c51ecdd86677132f
encrypted body you've received: 87afbab0f01b956c445b00d3ab1ecf09abf18e1d1ab980641a035ec801deff07d7b708d5f2747469b86ce6216358cb695df8c7d005265aab66f678d36f01af333f6f8715569c39adabf2688cdaabac3825a0ea2b588f7e083fcf62736a3064d395bed377254302f6279ec0550f117f63c971cc9320674f535694a27fc1f034cee57433c00fbec7cdd5d2daa5b12abfd96039715c98e8e8bca401011278c5c0d3ed30f710b11a1a9b8c67773eb34f0b050acbf97b32445e5bcc32399319bd947c42d1f9ba6051f9e998dfe2c61486af0e8584529d94fc3afda941557c58725aa9c7af03577446c6d6ff2e0894cef125c0e2366a3a96f11db5a4961ee92c8262b86836f6f8debf71c80e6b36a1bfd43cff72f144560258780f8bd97100e4e4b7cb9ef45d7b61133c3b197a2e12787e439ff197e2b567a3e178b9ff7155e2663989606cdc630dd835651ad18ab88f9494efc76a74e51ff01127ac4d83b3dd6f56cea34488ff8df204d470602a40cc970f5b1cfcf6c741abe20df05b392db817903191b6fc1a1b798cc369c5a76d151cbeaa
After decryption you should get:
{
"id": "6a82de8c-d7ae-4db5-972b-588808d5f111",
"type": "Deposit",
"status": "Complete",
"timestampms": "1640774872000",
"eid": "123456789",
"transactionHash": "0ccb68148928602274f94d1119bb5b3ac705e5b567396b948e4eb4cb12ee358996",
"currency": "ETH",
"amount": 1.35,
"organizationId": 1234567,
"eventTimestamp": 1640778972000,
"pledgeId": "e4a096e9-07e4-4a34-9e52-fff72fd27260",
"valueAtDonationTimeUSD": 4159.42
}
Decryption code snippet:
import crypto from "crypto";
const encryptedPayloadHex = '87afbab0f01b956c445b00d3ab1ecf09abf18e1d1ab980641a035ec801deff07d7b708d5f2747469b86ce6216358cb695df8c7d005265aab66f678d36f01af333f6f8715569c39adabf2688cdaabac3825a0ea2b588f7e083fcf62736a3064d395bed377254302f6279ec0550f117f63c971cc9320674f535694a27fc1f034cee57433c00fbec7cdd5d2daa5b12abfd96039715c98e8e8bca401011278c5c0d3ed30f710b11a1a9b8c67773eb34f0b050acbf97b32445e5bcc32399319bd947c42d1f9ba6051f9e998dfe2c61486af0e8584529d94fc3afda941557c58725aa9c7af03577446c6d6ff2e0894cef125c0e2366a3a96f11db5a4961ee92c8262b86836f6f8debf71c80e6b36a1bfd43cff72f144560258780f8bd97100e4e4b7cb9ef45d7b61133c3b197a2e12787e439ff197e2b567a3e178b9ff7155e2663989606cdc630dd835651ad18ab88f9494efc76a74e51ff01127ac4d83b3dd6f56cea34488ff8df204d470602a40cc970f5b1cfcf6c741abe20df05b392db817903191b6fc1a1b798cc369c5a76d151cbeaa';
const dataEncryptionKey = Buffer.from('ae97bfa25b4abcbb7d8722fc5c60747bf7687ec22d1463174f0176ad6daa96ec', 'hex');
const dataEncryptionKeyIV = Buffer.from('9663f5821170b172c51ecdd86677132f', 'hex');
const decryptText = (hexEncodedString: string) => {
const encryptedBuffer = Buffer.from(hexEncodedString, 'hex');
const decipher = crypto.createDecipheriv('aes-256-cbc', dataEncryptionKey, dataEncryptionKeyIV);
const decrypted = decipher.update(encryptedBuffer);
return Buffer.concat([decrypted, decipher.final()]).toString();
}
console.log(decryptText(encryptedPayloadHex));
Decryption code snippet
Copy
<?php
$hexEncryptedPayload = '87afbab0f01b956c445b00d3ab1ecf09abf18e1d1ab980641a035ec801deff07d7b708d5f2747469b86ce6216358cb695df8c7d005265aab66f678d36f01af333f6f8715569c39adabf2688cdaabac3825a0ea2b588f7e083fcf62736a3064d395bed377254302f6279ec0550f117f63c971cc9320674f535694a27fc1f034cee57433c00fbec7cdd5d2daa5b12abfd96039715c98e8e8bca401011278c5c0d3ed30f710b11a1a9b8c67773eb34f0b050acbf97b32445e5bcc32399319bd947c42d1f9ba6051f9e998dfe2c61486af0e8584529d94fc3afda941557c58725aa9c7af03577446c6d6ff2e0894cef125c0e2366a3a96f11db5a4961ee92c8262b86836f6f8debf71c80e6b36a1bfd43cff72f144560258780f8bd97100e4e4b7cb9ef45d7b61133c3b197a2e12787e439ff197e2b567a3e178b9ff7155e2663989606cdc630dd835651ad18ab88f9494efc76a74e51ff01127ac4d83b3dd6f56cea34488ff8df204d470602a40cc970f5b1cfcf6c741abe20df05b392db817903191b6fc1a1b798cc369c5a76d151cbeaa';
$cipher = 'aes-256-cbc';
$hexEncryptedKey = hex2bin( 'ae97bfa25b4abcbb7d8722fc5c60747bf7687ec22d1463174f0176ad6daa96ec' );
$hexEncryptedIV = hex2bin( '9663f5821170b172c51ecdd86677132f' );
// Decrypt the payload. NOTE this requires the Open SSL module installed on your server
// Also make sure that this function is accepting a RAW encoded string and NOT a base64 encoded string
// This is easily handled by making the 4th parameter TRUE
$decrypted_data = openssl_decrypt( hex2bin( $hexEncryptedPayload ), $cipher, $hexEncryptedKey, true, $hexEncryptedIV );
echo $decrypted_data;
Updated 2 days ago