โฐ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 integrations@thegivingblock.com.

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",
    "campaignId":"campaign-1"
  }
}

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). campaignId is the ID of the campaign for which the donation was made, in case it wasn't set then null is returned.

Event: Transaction converted (only crypto)

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",
    "campaignId":"campaign-1"
  }
}

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). campaignId is the ID of the campaign for which the donation was made, in case it wasn't set then null is returned.

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));

Last updated