Ping
A request we send you when a shop's sequence has grown, so that you call seq. It says which shop, and where the sequence ends now, and nothing else.
When we ping
A shop has one ping URL, which you set in the dashboard. We ping it:
- As soon as a shop's sequence grows: a payment, an act, or a change to
a subscriber, whoever made it. Changes that come together give one
ping, with the latest
"seq". - Every 5 minutes after the last ping, whether anything changed or
not, with the
"seq"as it is then.
A ping that fails is not sent again: the next change, or the next 5
minutes, brings another. Pings can arrive out of order, and more than
once with the same "seq". A shop without a ping URL gets no pings,
and has to call seq on a timer.
Request
POST {your ping URL}
We connect to your URL's host, on its port, or 443 for https and 80
for http, and send the ping as HTTP/1.0. With https, your
certificate must be valid for the host, as a browser would require: we
don't ping a host whose certificate fails. We don't follow redirects.
Use https: http works, but sends the ping, and its signature, in
the clear.
Headers
| HTTP header | Description |
|---|---|
Host |
Your URL's host, and its port if the URL has one. |
Content-Type |
application/json; charset=UTF-8 |
Content-Length |
The length of the body. |
X-Signature |
The body's signature, as base64: see Signature. |
Connection |
close. Each ping has a connection of its own. |
Body
| Field | Type | Description |
|---|---|---|
shopid |
integer | The shop whose sequence has grown. |
seq |
integer | Where the shop's sequence ends now: the "seq" that seq returns when you have read all of it. |
{"shopid":1234,"seq":164}
Signature
X-Signature is the base64, with padding, of the HMAC-SHA256 of the
body as sent, with the shop's API key as the HMAC's key: the whole key,
as you give it to Basic authentication before base64, with the shop ID
in front, such as 1234:FhnIJVS2ZgRi2B35.... Check it before you
trust the ping:
want := Base64(HMAC-SHA256(key: apikey, message: rawbody))
if not ConstantTimeEqual(want, headers["X-Signature"]) then
return 403
end if
ping := JSONDecode(rawbody)
if ping.seq > LoadSeq() then
WakeSynchronizer(ping.seq)
end if
return "ok"
Sign the bytes as received, before any parsing, and compare in constant time. Receiving pings has the mistakes to avoid.
A ping carries no data but the shop's ID and a number, so a forged one can do no worse than make you call seq. The signature lets you skip even that.
Response
Answer with 200 OK and the body ok, at once. We give a ping 7
seconds from connecting until the first byte of your answer, and then
hang up, so synchronize after answering, or in another process: see
Synchronizing with seq.
We don't read your answer beyond its first byte, and don't act on it: a ping that you answer with an error, or not at all, is not sent again, as the next one comes anyway.