Developer guide · API v1

Accept card payments with GetPay payment links

Create a payment link from your server, send your customer to it, and get notified when they pay. GetPay hosts the payment page, talks to the bank, and reports every result back to you.

Every example on this page uses this address. Paste yours from the GetPay dashboard (Domain tab): each store has its own, on its own domain.

01How it works

A payment link is a hosted payment page for one order. Your server creates it with your order details, and GetPay takes care of the rest.

  1. Your serverCreates a payment link with the items, amount and your order number. GetPay returns a url.
  2. Your websiteRedirects the customer to that url.
  3. CustomerReviews the order, enters their details and pays by card on the bank’s secure page. If the card is declined, they can try again on the same link.
  4. GetPayChecks the result with the bank, sends the customer to your success_url, and sends a payment_link.completed webhook to your server.
  5. Your serverConfirms the payment and fulfils the order.
sequenceDiagram
    participant S as Your server
    participant C as Customer
    participant G as GetPay
    participant B as Bank (vPOS)
    S->>G: POST /payment-links
    G-->>S: { id, url }
    S->>C: Redirect to url
    C->>G: Opens payment page, presses Pay
    G->>B: Register payment
    C->>B: Enters card, 3-D Secure
    B->>G: Result
    G->>C: Redirect to success_url
    G->>S: Webhook payment_link.completed
    S->>G: GET /payment-links/{id} (confirm)
      

02Before you start

Your domain and base URL

The API runs on your own domain, not on getpay.am. In the GetPay dashboard open the Domain tab, enter your domain (for example shopx.am) and add the four DNS records it shows, all pointing to the GetPay IP address:

TypeNameUsed for
AapiYour API base URL: https://api.shopx.am/api/v1
AmerchantYour dashboard
AcheckoutPayment pages your customers pay on
AreceiptReceipt pages

Then press Check connection. Once all records are found, the dashboard shows your API base URL. Requests sent to any other address, including api.getpay.am, are refused with 403.

API keys

Find your keys in the GetPay dashboard under Home → API keys. You have two, one per environment:

KeyUse it for
sk_sandbox_…Building and testing. Payments end on the GetPay test page; no bank is contacted and no money moves.
sk_live_…Real payments. Works after your merchant account goes live and a bank is connected.

Send the key in the Authorization header of every request:

Keep secret keys on your server.Never put them in a browser, a mobile app or a public repository. Store them in an environment variable such as GETPAY_SECRET_KEY.

Conventions

  • Amounts are in minor units. 150000 means 1,500.00 AMD. Always send whole numbers.
  • Currency: the currencies GetPay has enabled — GET /meta/currencies (AMD for now). Others return CURRENCY_NOT_SUPPORTED.
  • Requests and responses are JSON. Send Content-Type: application/json.
  • Times are UTC in ISO 8601, for example 2026-09-30T10:15:00Z.
  • IDs are UUIDs. Store the payment link id next to your order.

03Create a payment link

POST/payment-links

Call this from your server when the customer is ready to pay. Send what they are buying, who they are (if you know), and where to send them afterwards.

Parameters

Send either line_items or amount, not both. Everything else is optional.

FieldRequiredDescription
line_items[]One of the twoProducts the customer pays for, up to 100. The total is calculated from them.
line_items[].nameYesProduct name shown on the payment page. Up to 255 characters.
line_items[].unit_amountYesPrice per unit in minor units.
line_items[].quantityNo1 to 10,000. Default 1.
line_items[].descriptionNoShort text under the name. Up to 1,000 characters.
line_items[].image_urlNoPublic http(s) image URL shown next to the item.
line_items[].product_idNoYour SKU or product ID.
line_items[].metadataNoKey/value strings for your own use.
line_items[].ehdmNoFiscal receipt data. Required while EHDM is enabled, see Fiscal receipts.
amountOne of the twoFixed total in minor units when you don’t send items. Requires title or description.
currencyNoAMD (default), or another currency enabled by GetPay (see GET /meta/currencies).
titleNoHeadline on the payment page, e.g. “Order #1001”.
descriptionNoLonger text on the page. Up to 2,000 characters.
merchant_referenceNoYour order number. Returned on the link, its payments and every webhook, and searchable.
languageNoPage language: hy (default), en or ru. The customer can switch it.
customer.emailNoPre-fills the payment page.
customer.nameNoPre-fills the payment page.
customer.phoneNoPre-fills the payment page.
customer.external_idNoYour customer ID. Never shown to the customer.
customer.addressNoline1, line2, city, region, postal_code, country (2-letter code, e.g. AM).
customer_fieldsNoWhat the page asks for. For each of email, name, phone, address: required, optional or hidden. Default: email required, name and phone optional, address hidden.
success_urlNoWhere the customer goes after paying. GetPay adds payment_link_id, payment_id, receipt_number and receipt_url to it (see Receipts).
cancel_urlNo“Back to store” link on the payment page.
payment_methodsNoLimit the methods offered, e.g. ["ameriabank"]. Default: all you have enabled.
expires_in_minutesNo30 to 43,200 (30 days). Default 1,440 (24 hours).
metadataNoUp to 50 keys. Keys up to 40 characters, values up to 500. Returned on the link, its payments and webhooks.
ehdmNoFiscal receipt options: skip, skip_reason, item. See Fiscal receipts.

Response

201 Created with the payment link. Save id with your order and send the customer to url.

If the request times outyou may not know whether the link was created. Look it up by your order number before creating another: GET /payment-links?merchant_reference=order-1001.

04Send the customer to the payment page

Redirect the customer’s browser to the url from the response. The page shows your business name, the items and total, asks for the fields you chose, and offers card payment. The customer can switch between Armenian, English and Russian.

What happens next

OutcomeWhere the customer ends up
paidYour success_url with ?payment_link_id=…&payment_id=…&receipt_number=…&receipt_url=… added. Without a success_url, a GetPay confirmation page with a link to the receipt.
declinedBack on the payment page with the reason (for example “Not enough money on the card”). They can try again or use another card until the link expires.
clicks backYour cancel_url. The link stays open, so they can come back to it.
The redirect is not proof of payment.Anyone can open your success page with any parameters. Always confirm on your server before fulfilling the order.

05Confirm the payment

Use either way, or both. Webhooks are the reliable way because they arrive even if the customer closes the browser before reaching your success page.

  • Webhook: handle payment_link.completed (see Webhooks).
  • API: when the customer lands on your success_url, fetch the link.
GET/payment-links/{id}

Fulfil the order only when status is complete and payment_status is paid, and the amount_total and currency match your order. This call also re-checks a pending payment with the bank.

Payment link statuses

statusMeaning
openWaiting for payment. Declined attempts leave it open.
completePaid. payment_status is paid and payment holds the successful payment.
expiredexpires_at passed before payment.
canceledYou closed it with POST /payment-links/{id}/expire.

Payment statuses

Each attempt to pay is a payment, returned in the link’s payment field and by the payments API.

statusMeaning
pendingThe customer is paying, or the bank has not confirmed yet.
succeededMoney was taken from the card.
failedDeclined. failure_reason says why. The customer may retry.
refundedFully refunded.
partially_refundedPart of the amount was refunded. See amount_refunded.
canceledAbandoned, for example the link was canceled mid-payment.
reversedYou reversed the whole payment at the bank within 72 hours. See reversed_at.

06Webhooks

GetPay sends an HTTPS POST to your server when something happens, so you don’t have to poll. Each request is signed with a secret only you and GetPay know.

Set up an endpoint

  • In the dashboard, open Webhooks, choose Sandbox or Live, and add your URL, e.g. https://your-site.am/webhooks/getpay.
  • Choose all events or only the ones you need.
  • Copy the signing secret (whsec_…) and store it on your server as GETPAY_WEBHOOK_SECRET. It is different from your API key.
  • Press Send test to check your endpoint. The dashboard shows every delivery with your server’s response.

Live endpoints must use https://. Sandbox endpoints may use http://. GetPay cannot reach localhost; to receive events on your computer while developing, use a tunnel such as ngrok http 8001 and register the public URL it gives you.

Events

EventWhendata.object
payment_link.completedThe link was paid. Fulfil the order on this event.payment link
payment.succeededA card payment was approved.payment
payment.failedA payment attempt was declined. The customer may still retry and pay.payment
payment.refundedA payment was fully or partially refunded.payment
payment.reversedA whole payment was reversed at the bank.payment
receipt.createdA receipt was issued because a payment was paid. Send url to your customer.receipt
receipt.updatedA receipt changed after a refund or reversal.receipt
payment_link.canceledYou closed the link through the API.payment link
webhook.testYou pressed Send test in the dashboard.test message

Request format

HeaderValue
GetPay-Signaturet=1790755839,v1=5f2b…: the send time and an HMAC-SHA256 signature.
GetPay-Event-IdUnique event ID. Use it to ignore duplicates.
GetPay-Event-TypeFor example payment_link.completed.
Content-Typeapplication/json

data.object has exactly the same shape as the API response for that object.

Verify the signature

The signature is HMAC-SHA256(secret, t + "." + raw_body) in hex. Compute it over the raw request body (re-encoded JSON will not match), compare in constant time, and reject requests older than 5 minutes.

Respond and retry

  • Return any 2xx within 10 seconds. Do slow work (emails, stock updates, ERP sync) in a background job after responding.
  • Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and 24 hours. After that the delivery is marked failed, and you can resend it from the dashboard.
  • Expect duplicates. The same event can arrive more than once. Store processed GetPay-Event-Id values and skip repeats.
  • Don’t rely on order. Events can arrive out of order. Use the status inside data.object, or fetch the latest from the API.
  • Double-check if unsure. Fetch the event with GET /events/{id} using your secret key.

07Test in sandbox

With your sk_sandbox_… key, everything works like live, but pressing Pay opens the GetPay test page instead of a bank. You choose how the payment ends. Webhooks, redirects, decline messages and refunds behave exactly as they will in live mode. No bank credentials are needed.

Test page buttonResultBank code
Succeed paymentsucceeded with test card 424242******424200
Not enough moneyfailed0116
Expired cardfailed0101
Declined by card issuerfailed0100
3-D Secure failedfailed0-2006
Leave processingpending: the bank has not confirmed yet; finish it later from the test page—

Test these before going live: a successful payment, a decline followed by a successful retry, a customer who closes the browser after paying (your webhook must still fulfil the order), and a refund.

08Refunds and reversals

There are two ways to give money back. Both return it to the customer’s card through the bank.

ActionAmountWhenResult
ReverseAlways the whole paymentWithin 72 hours of the payment starting, and only if nothing was refunded yetreversed
Full refundEverything not yet refundedAny time after a successful paymentrefunded
Partial refundAny amount up to what remains; repeatableAny time after a successful paymentpartially_refunded, then refunded when nothing remains

Every payment tells you what is possible right now, so you don’t have to track the rules yourself:

FieldMeaning
can_reversetrue while a reversal is possible.
reversible_untilThe reversal deadline: 72 hours after the payment started. Show it to your staff.
can_refundtrue while something can still be refunded.
amount_refundableThe most you can still refund, in minor units.
amount_refundedRefunded so far.
reversed_atWhen the payment was reversed.

Reverse a payment

POST/payments/{id}/reverse

Cancels the whole payment at the bank. Use it when an order is canceled shortly after payment. After 72 hours, or once anything has been refunded, the request returns 409 and you need a refund instead. A payment.reversed webhook follows.

Refund a payment

POST/payments/{id}/refund

Send {"amount": 500000} for a partial refund of up to amount_refundable. Send an empty body or {} for a full refund of everything that remains. You can make several partial refunds. A payment.refunded webhook follows each one.

Both requests return the updated payment. The payment ID is in the link’s payment.id field and in the payment_id added to your success URL.

09Receipts

Every paid payment gets a receipt, much like Stripe receipts: a short number the customer can quote, such as 4821-0937, and a receipt page hosted by GetPay on your domain at https://receipt.your-domain.am/…. The page shows your business details, the items, the card used, any refunds, and a button to save it as a PDF. It is available in Armenian, English and Russian.

The receipt is issued the moment the payment is paid, so the link is already there when the customer returns to your site and when the webhooks arrive. There is nothing to create or enable.

Where you get the receipt link

ChannelWhat you get
Success URLreceipt_number and receipt_url are added to your success_url, next to payment_link_id and payment_id. Use them to show a “View receipt” link on your thank-you page.
Webhooksreceipt.created when the payment is paid and receipt.updated after each refund or reversal. data.object is the full receipt. Every payment.* and payment_link.* event also carries receipt_number and receipt_url on the payment.
APIreceipt_number and receipt_url on every payment (GET /payments/{id} and the link’s payment), and the full receipt at GET /payments/{id}/receipt.
Show the receipt link, but don’t trust it.Like everything in the redirect, these parameters can be changed by anyone. Confirm the payment on your server (see Confirm the payment) before fulfilling the order. For emails and your account area, take receipt_url from the webhook or the API.

Receipt object

FieldMeaning
numberReceipt number shown to the customer. Unique within your account.
urlPublic receipt page. Same value as receipt_url on the payment.
statusThe payment status: succeeded, partially_refunded, refunded or reversed.
amountAmount paid, in minor units.
amount_refundedRefunded so far. Each refund is listed in refunds[] with its date.
amount_netWhat the customer finally paid: amount − amount_refunded, or 0 after a reversal.
line_items[]The items from the payment link.
merchantYour business name, legal name, tax ID (ՀՎՀՀ), address and support contacts, taken from your dashboard profile.
customerEmail, name and phone the customer entered.
payment_methodBank, masked card number, cardholder name, approval code and RRN.
paid_at, issued_atWhen the payment was made and when the receipt was issued.
Keep receipt links private.Anyone with the link can open the receipt, and the link cannot be guessed. Share it only with the customer. Sandbox receipts open on the same page with a “Test mode” banner.

10Fiscal receipts (EHDM)

If your business must issue cash register receipts, GetPay can register every successful payment with the State Revenue Committee through the PayX electronic cash register (Է-ՀԴՄ, ehdm.am). You need your own contract with PayX. Connect the API login they give you in the dashboard under Settings → EHDM.

While EHDM is enabled, every payment link must say what is sold: the classifier code, the name in Armenian and the unit. GetPay issues the fiscal receipt automatically once the payment succeeds, and the return receipt after a reversal or a full refund.

Check whether EHDM is enabled

GET/ehdm

enabled: true means your payment links must carry fiscal data (or skip it). dep is the tax department you chose in your EHDM settings; it is used for line items that send no dep.

Fiscal data on a payment link

FieldRequiredDescription
line_items[].ehdm.adg_codeYes*Classifier code: ԱԴԳՏ for services (e.g. 85.59 — courses) or ԱՏԳ ԱԱ for goods (e.g. 5401). Search with GET /ehdm/adg-codes?q=….
line_items[].ehdm.good_nameYes*Name in Armenian as printed on the receipt, up to 50 characters. Defaults to line_items[].name when it fits.
line_items[].ehdm.unitYes*One of: հատ, կգ, գրամ, լիտր, մետր, ք/մ, խ/մ, մլ, այլ — piece, kg, gram, liter, meter, m², m³, ml, other. Printed on the receipt as sent.
line_items[].ehdm.good_codeYes*Internal code — your own product code or barcode (up to 50 characters), printed on the receipt as Ն/Կ.
line_items[].ehdm.depNoTax department: 1 VAT, 2 VAT-exempt, 3 turnover tax, 7 micro-enterprise. Defaults to the tax department in your EHDM settings.
ehdm.itemNoThe same fields for an amount link without line items. good_name defaults to title.
ehdm.skipNotrue creates the link without a fiscal receipt, e.g. when it is issued elsewhere. ehdm.skip_reason is an optional note.
* Required only while EHDM is enabled. If something is missing, the link is not created and you get EHDM_FIELDS_REQUIRED with the list of missing fields.

After the payment

The receipt is registered in the background, usually within seconds. The customer’s receipt page (receipt_url) shows it like a cash register receipt, with the fiscal number, QR code and a link to the PayX PDF. You get it in three places:

ChannelWhat you get
APIfiscal_receipt on every payment, and GET /payments/{id}/fiscal-receipt.
Webhooksreceipt.updated when the fiscal receipt is issued and when it is returned. data.object.fiscal holds it.
DashboardTransaction details show the fiscal receipt, its status and a Retry button.

Fiscal receipt statuses

StatusMeaning
pendingWaiting to be registered (or retrying).
issuedRegistered with the tax authority.
failedCould not be registered after all retries. Retry it from the dashboard or the API.
reverse_pendingReturn receipt is being issued after a reversal or full refund.
reversedReturn receipt issued.
reverse_failedReturn receipt could not be issued. Retry it.
canceledThe payment was returned before the receipt was registered, so nothing was registered.
The payment never waits for the receipt.If PayX is unavailable, GetPay retries for about 10 hours, then sets failed; retry with POST /payments/{id}/fiscal-receipt/retry. A partial refund can’t be returned per product automatically: needs_manual_return becomes true and you issue the return receipt in PayX.
Sandbox receipts are simulated.They have livemode: false and never reach PayX or the tax authority, so you can test the whole flow, including validation errors.

11API reference

All endpoints use your secret key. A sandbox key only sees sandbox data, and a live key only sees live data.

EndpointDescription
POST /payment-linksCreate a payment link.
GET /payment-linksList links, newest first.Filters: status, merchant_reference, customer_external_id, limit (1–100, default 20), offset.
GET /payment-links/{id}Retrieve a link and its latest payment.
POST /payment-links/{id}/expireClose an unpaid link so it can no longer be paid.
GET /paymentsList payments.Filters: status, merchant_reference, payment_link_id, limit, offset.
GET /payments/{id}Retrieve a payment, including card details (masked number, holder name, approval code, RRN).
GET /payments/{id}/receiptRetrieve the receipt of a paid payment (404 until it is paid). Same object as in receipt.* webhooks.
POST /payments/{id}/refundRefund all (no amount) or part (amount) of a payment.
POST /payments/{id}/reverseReverse the whole payment within 72 hours.
GET /meta/currenciesCurrencies GetPay knows; payment links can be created in those with is_enabled: true. No key needed.
GET /ehdmWhether EHDM is enabled for your account, and your tax department.
GET /ehdm/adg-codesSearch classifier codes.Filters: q (code prefix or part of the name), kind (adgt or atg), limit.
GET /payments/{id}/fiscal-receiptFiscal receipt of a payment.
POST /payments/{id}/fiscal-receipt/retryRetry a failed or reverse_failed fiscal receipt.
GET /eventsList events, newest first.Filters: type, limit, offset.
GET /events/{id}Retrieve one event, for example to confirm a webhook you received.

Lists return { "object": "list", "items": [...], "total", "limit", "offset" }.

12Errors

Errors return a JSON body with the HTTP status, a stable message_code to check in your code, and a readable message.

Statusmessage_codeMeaning
400BAD_REQUESTThe request can’t be processed, e.g. an unsupported currency or no payment method enabled. Read message.
400EHDM_FIELDS_REQUIREDEHDM is enabled and fiscal data is missing. details.missing lists the fields, e.g. line_items[0].ehdm.adg_code.
400CURRENCY_NOT_SUPPORTEDThe currency is not enabled by GetPay. details.supported lists the currencies you can use.
400EHDM_CURRENCY_NOT_SUPPORTEDFiscal receipts are AMD only. Use AMD or send ehdm.skip: true.
401UNAUTHORIZEDMissing or invalid secret key.
403FORBIDDENThe key can’t do this, e.g. a live key before your account is live.
404NOT_FOUNDNo such object for this key and environment.
409CONFLICTNot possible in the current state, e.g. reversing after 72 hours, refunding an unpaid payment or expiring a paid link. message says what to do instead.
422VALIDATION_ERRORA field is missing or invalid. details.errors lists each field.
502PAYMENT_GATEWAY_ERRORThe bank rejected or didn’t answer a refund. Safe to retry later.

13Go-live checklist

  • Your domain is connected under Domain, and your server uses the API base URL shown there.
  • Your merchant account has completed onboarding and is switched to live in the dashboard.
  • Your bank (Ameriabank vPOS) credentials are connected and enabled under Payment gateways → Live.
  • Your server uses the sk_live_… key from an environment variable, never from code.
  • A live webhook endpoint is added with an https:// URL, and its whsec_… secret is on your server.
  • Your webhook handler verifies signatures, rejects old timestamps and ignores duplicate event IDs.
  • You fulfil orders only after confirming complete / paid and matching amount and currency.
  • Your thank-you page or order email shows the customer the receipt_url.
  • If you use EHDM: PayX is connected and enabled under Settings → EHDM, and your links send line_items[].ehdm.
  • You made one small real payment and refunded it.