Callbacks
It is a good idea to create an IPN listener page on your website and then specify the URL of the listener page in a "Settings → API" section in your Cryptopay account.
It is also important to note that you should use only https URL. Cryptopay then sends a secure POST request containing payment details of all transaction-related events to the URL.
Handling callbacks correctly is crucial to make sure your integration’s business logic works as expected. It is highly recommended to validate payment statuses and callbacks before acting on it inside your system.
The IPN listener page contains a custom script or a program that gets messages, validates them with Cryptopay, and then passes them to various backend applications for processing.

Acknowledge events immediately

If your callbacks script performs complex logic, or makes network calls, it’s possible that the script would time out before Cryptopay sees its complete execution. Ideally, your callback handler code (notification of an event by returning a 200 status code) is separate from any other logic you do for that event.

Handle duplicate events

Callback endpoints might occasionally receive the same event more than once. We advise you to guard against duplicated event receipts. One way of doing this is logging the events you’ve processed, and then not processing the logged ones.

Retry logic

Cryptopay IPN server expects to get a 200 status code from you within 10 seconds. If the response code is different from 200 or a deadline is exceeded, we deliver your callbacks for up to one and a half days with an exponential backoff:
30 + num ^ 4 + num seconds where num is 0 to 19 retry
1
0d 00h 00m 30s
2
0d 00h 00m 32s
3
0d 00h 00m 48s
4
0d 00h 01m 54s
5
0d 00h 04m 50s
6
0d 00h 11m 00s
7
0d 00h 22m 12s
8
0d 00h 40m 38s
9
0d 01h 08m 54s
10
0d 01h 50m 00s
11
0d 02h 47m 20s
12
0d 04h 04m 42s
13
0d 05h 46m 18s
14
0d 07h 56m 44s
15
0d 10h 41m 00s
16
0d 14h 04m 30s
17
0d 18h 13m 02s
18
0d 23h 12m 48s
19
1d 05h 10m 24s
20
1d 12h 12m 50s
Copied!

Security

Every callback request contains a X-Cryptopay-Signature header:
1
"X-Cryptopay-Signature": "7c021857107203da4af1d24007bb0f752e2f04478e5e5bff83719101f2349b54"
Copied!
This header contains the hex encoded SHA256 HMAC signature of the callback request body string, computed using your callback Secret as the key.
  1. 1.
    You receive a callback
  2. 2.
    You use SHA256 for hashing its body string with a callback secret
  3. 3.
    You compare X-Cryptopay-Signature value to the hash you've got after hashing the callback body string + callback secret
Ruby
1
class CryptopayCallbackVerifier
2
def initialize(secret)
3
@secret = secret
4
end
5
6
def verify(body, signature)
7
expected_signature = OpenSSL::HMAC.hexdigest('SHA256', @secret, body)
8
secure_compare(signature, expected_signature)
9
end
10
11
private
12
13
# Constant-time comparison algorithm to prevent timing attacks
14
def secure_compare(str1, str2)
15
return false if str1.empty? || str2.empty? || str1.bytesize != str2.bytesize
16
17
l = str1.unpack("C#{str1.bytesize}")
18
19
res = 0
20
str2.each_byte { |byte| res |= byte ^ l.shift }
21
res.zero?
22
end
23
end
24
25
secret = 'hzeRDX54BYleXGwGm2YEWR4Ony1_ZU2lSTpAuxhW1gQ'
26
verifier = CryptopayCallbackVerifier.new(secret)
27
28
# Raw callback body
29
body = '{"type":"Invoice","event":"status_changed","data":{"id":"ff48eeba-ab18-4088-96bc-4be10a82b994","status":"completed","status_context":null,"address":"rs9pE6CnNLE8YiTgTwbAk1AkFyS3opsm7K?dt=701","price_amount":"1.0","price_currency":"EUR","pay_amount":"3.113326","pay_currency":"XRP","paid_amount":"3.113326","exchange":{"pair":"XRPEUR","rate":"0.3212"},"transactions":[{"txid":"3EA591FED2F1F61263CB66AAC6BCF520B0714A08F2481D56DE267F31E0C782B9","risk":null}],"name":null,"description":null,"metadata":null,"custom_id":null,"success_redirect_url":null,"created_at":"2019-04-09T15:22:09+00:00","expires_at":"2019-04-09T15:32:09+00:00"}}'
30
31
# Value of X-Cryptopay-Signature header
32
signature = '7c021857107203da4af1d24007bb0f752e2f04478e5e5bff83719101f2349b54'
33
34
verifier.verify(body, signature) # => true
Copied!
Every new callback request will have a new value of the X-Cryptopay-Signature header. Make sure you are comparing the hash to the right header.
A callback secret is available in the "Settings → API" section in your Cryptopay account.

Callback IPs

We send all our callbacks from these IP:
  • Production - 63.33.129.150
  • Sandbox - 63.33.105.227
You can use these IPs as a whitelist for receiving callbacks.
Last modified 4mo ago