.. meta:: :description: Cashier Intent call of Neonpay: ask which payment methods of a Master Endpoint a player may use before rendering the Connecting Party cashier, with amount limits and refusal reasons. .. _cashier_intent: Cashier Intent ############## .. role:: ex .. role:: code .. _cashier_intent_introduction: Introduction ==================================================== A Connecting Party that draws its own cashier — the screen where a player picks how to deposit or withdraw — has to know which payment methods to show before the player has chosen anything. The methods live on the :ref:`Auxiliary Endpoints` of a :ref:`Master Endpoint`, and which of them a particular player may use depends on who the player is: their wallet currency, country, KYC status and categories, as the Connecting Party Player Account Management system (PAM) knows them. The **intent** call answers exactly that question. The Connecting Party server sends the player identifiers to the Master Endpoint, Neonpay checks the player session with the PAM configured for that Master Endpoint and returns the list of methods for the requested direction, each with its amount limits and a flag saying whether the player may use it right now. The call has three properties worth knowing up front. Nothing is created Opening the cashier is not an order. The call creates no transaction, reserves nothing and leaves no trace in the order list, so a cashier the player opens and abandons costs nothing but the call itself. Nothing is cached The player session is checked with PAM on every call. A session the Connecting Party revokes stops producing methods on the very next call, and a later payment checks the player again independently of any intent answer, see :ref:`Next Step: Payment`. Personal data stays on the server side The answer carries only what a cashier needs to draw itself — wallet currency and balance, categories, KYC status and the methods. The player's name, address, date of birth, phone and email are not returned and are not stored. .. note:: The interaction between Neonpay and PAM is not part of the Connecting Party integration and is not described here. For the cashier it is a single step inside the intent call. .. _cashier_intent_prerequisites: What to Obtain from Neonpay ==================================================== :ex:`MASTERENDPOINTID` and :ex:`MERCHANTLOGIN` in the examples are placeholders. Real values are provided by Neonpay on onboarding, independently for the sandbox and the production environment. .. list-table:: :widths: 25, 75 :header-rows: 1 :class: longtable * - Parameter - Description * - Master Endpoint ID - Identifier of the Master Endpoint the cashier is opened on. Used in the URL of the intent call. The methods returned are its Auxiliary Endpoints. * - Merchant login - Merchant login provided by Neonpay. Sent as :ex:`oauth_consumer_key` when signing requests. The Master Endpoint must belong to this login. * - RSA key pair - The **private** key signs the requests and never leaves the Connecting Party server. The **public** key is sent to the support manager and is configured on the Master Endpoint. * - PAM integration - Enabled on the Master Endpoint by Neonpay support, together with the connection to the Connecting Party PAM and the rules of each method (countries, KYC statuses, player categories). Until it is enabled the intent call is refused with error :ex:`20`. The keys are generated with a `Key-Pair-Factory tool `_ or :ref:`OpenSSL`, see :ref:`Generating Key Pair`. The signing procedure is described in :ref:`OAuth RSA-SHA256`. .. _cashier_intent_flow: Cashier Flow ==================================================== The diagram shows a deposit. A payout follows the same path with :ex:`operation=payout`. .. uml:: :align: center skinparam roundcorner 20 skinparam sequenceArrowThickness 2 skinparam ParticipantPadding 30 actor Payer as Customer participant "Connecting Party\nCashier" as Page participant "Connecting Party\nServer" as Merchant participant "Neonpay" as g participant "Connecting Party\nPAM" as Pam autonumber Customer -> Page: Open cashier Page -> Merchant: Which methods? activate Merchant == Intent == Merchant -> g: /api/v4/intent/ with pam_user_id,\npam_session_id, operation activate g g -> Pam: Check player session activate Pam Pam --> g: Player profile deactivate Pam g -> g: Select methods g --> Merchant: Methods with limits\nand availability deactivate g Merchant --> Page: Methods to show deactivate Merchant == Payment == Customer -> Page: Pick method, enter amount Page -> Merchant: Pay with the chosen method activate Merchant Merchant -> g: /api/v4/sale/ on the method end-point-id\nwith pam_user_id, pam_session_id activate g g --> Merchant: Order ID g -> Pam: Check player, reserve the amount activate Pam Pam --> g: Approved deactivate Pam g -> g: Process payment g -> Pam: Report the payment result activate Pam Pam --> g: Accepted deactivate Pam g -> Merchant: Final status\n(callback or status request) deactivate g Merchant --> Page: Payment result deactivate Merchant | (3) The request is signed with OAuth 1.0a RSA-SHA256 and addressed to the Master Endpoint, see :ref:`Request`. | (4,5) Neonpay checks the player session with the PAM configured on the Master Endpoint. This happens on every call; a refusal or an outage of PAM is reported in the answer, not as an error, see :ref:`Reading the Answer`. | (6) Methods are filtered by the player country, currency and the requested direction, then checked against the amount, KYC status and player categories, see :ref:`Method Selection`. | (7) The answer is JSON: the methods and, when the player cannot pay, the reason, see :ref:`Response`. | (11) The payment is a regular payment call addressed to the :ex:`end-point-id` of the chosen method, not to the Master Endpoint, see :ref:`Next Step: Payment`. | (12) The response is asynchronous: it carries the Order ID, but not the payment status. See :ref:`Async-Response`. | (13,14) Before the payment reaches 3-D Secure or the acquirer, Neonpay checks the player with PAM again and asks it to reserve the amount. If PAM refuses, the payment is declined and the acquirer is not contacted. | (15) The payment is processed as usual. | (16,17) Once the payment reaches its final status, Neonpay reports the result to PAM: a successful payment is settled against the reservation, and a declined or failed one releases it. The Connecting Party does not take part in this step, and delivery to PAM is retried on its own. | (18) The final status is delivered as a :ref:`Connecting Party Callback` or requested with an :ref:`Order Status Request`. .. _cashier_intent_request: Request ==================================================== .. list-table:: :widths: 50, 50 :header-rows: 1 :class: longtable * - Integration - Production * - :ex:`https://sandbox.transaction.click/paynet/api/v4/intent/MASTERENDPOINTID` - :ex:`https://gate.transaction.click/paynet/api/v4/intent/MASTERENDPOINTID` The request is a :code:`POST` with a form-encoded body, signed with :ref:`OAuth 1.0a RSA-SHA256` like other v4 calls: the signature is computed over the request parameters, which is why the body is not JSON. .. code-block:: http POST /paynet/api/v4/intent/MASTERENDPOINTID HTTP/1.1 Host: sandbox.transaction.click Content-Type: application/x-www-form-urlencoded Authorization: OAuth oauth_consumer_key="MERCHANTLOGIN", oauth_signature_method="RSA-SHA256", oauth_timestamp="...", oauth_nonce="...", oauth_version="1.0", oauth_signature="..." pam_user_id=player-1001&pam_session_id=7c1e0d52-5b1f-4f0a-9d2b-3a6c8e4f1b20&operation=deposit&amount=50.00¤cy=EUR&country=SE .. list-table:: :widths: 20, 10, 70 :header-rows: 1 :class: longtable * - Parameter - Required - Description * - :ex:`pam_user_id` - yes - The player as the Connecting Party PAM knows them. Up to 100 characters. * - :ex:`pam_session_id` - yes - The player session on the Connecting Party side, checked with PAM on every call. Up to 255 characters. * - :ex:`operation` - yes - :ex:`deposit` or :ex:`payout`, case-insensitive. Only Auxiliary Endpoints configured for that direction are returned. Any other value is rejected with error :ex:`1`. * - :ex:`amount` - no - The amount the player intends to pay or withdraw, in the wallet currency. When omitted, methods are returned without checking their amount limits — a cashier usually opens before the player has typed an amount. * - :ex:`currency` - no - ISO 4217 code. Used only when PAM reports no wallet currency; the wallet currency reported by PAM always wins. There is no conversion: methods in another currency are not offered. * - :ex:`country` - no - ISO 3166-1 alpha-2 or alpha-3 code. Used only when PAM reports no country. An unknown code is ignored and does not narrow the list. * - :ex:`locale` - no - Preferred locale, e.g. :ex:`sv_SE`. Up to 16 characters. Accepted for forward compatibility; it does not affect the answer at the moment. Parameter names are also accepted with hyphens: :ex:`pam-user-id`, :ex:`pam-session-id`. .. warning:: Send non-empty :ex:`pam_user_id` and :ex:`pam_session_id`. A missing parameter is rejected with a precise message (error :ex:`1`), but an empty value may surface as an internal error (:ex:`102`) that is harder to diagnose. .. _cashier_intent_response: Response ==================================================== A processed request is answered with :code:`200` and a JSON body, :ex:`Content-Type: application/vnd.pay+json;charset=UTF-8`, regardless of the :ex:`Accept` header. Fields without a value are omitted rather than sent as :ex:`null`. .. code-block:: http HTTP/1.1 200 OK Content-Type: application/vnd.pay+json;charset=UTF-8 { "type": "intent", "serial-number": "00000000-0000-0000-0000-000002d9b22a", "end-point-id": 221298, "user-currency": "EUR", "user-balance": 500.00, "user-categories": ["VIP"], "kyc-status": "APPROVED", "methods": [ { "end-point-id": 221296, "identifier": "card", "name": "Card", "currency": "EUR", "flow-type": "DEPOSIT", "logo-url": "https://merchant.com/logos/card.svg", "preferred": true, "available": true, "min-amount": 1.00, "max-amount": 1000.00, "saved-instruments-only": false }, { "end-point-id": 221299, "identifier": "bank-transfer", "name": "Bank transfer", "currency": "EUR", "flow-type": "DEPOSIT", "preferred": false, "available": false, "refusal": "AMOUNT_BELOW_MINIMUM", "min-amount": 100.00, "saved-instruments-only": false } ], "saved-methods": [] } .. list-table:: :widths: 25, 75 :header-rows: 1 :class: longtable * - Field - Description * - :ex:`type` - Always :ex:`intent`. * - :ex:`serial-number` - Request serial number, as on every API response. Quote it to support: it is the key the call is traced by, since the call creates no order. * - :ex:`end-point-id` - The Master Endpoint the cashier was opened on. * - :ex:`user-currency` - Wallet currency, ISO 4217 — the currency the cashier works in and every method is in. * - :ex:`user-balance` - Wallet balance as PAM reported it. Omitted when PAM did not report one. * - :ex:`user-categories` - Player categories as PAM reported them. Empty array when there are none. * - :ex:`kyc-status` - KYC status as PAM reported it. Omitted when PAM did not report one. * - :ex:`methods` - Methods for the requested direction, in the order configured on the Master Endpoint. May be empty, see :ref:`Reading the Answer`. * - :ex:`saved-methods` - Instruments the player has already used. Part of the contract, **always empty at the moment**. * - :ex:`refusal` - Why the player cannot pay now. Omitted when they can. See :ref:`Refusal Codes`. Each element of :ex:`methods` is one Auxiliary Endpoint. .. list-table:: :widths: 25, 75 :header-rows: 1 :class: longtable * - Field - Description * - :ex:`end-point-id` - Auxiliary Endpoint ID — where the payment with this method is addressed to, see :ref:`Next Step: Payment`. * - :ex:`identifier` - Method identifier as configured on the Master Endpoint, e.g. :ex:`card`. Stable, suitable for the cashier's own mapping of icons and texts. * - :ex:`name` - Display name as configured on the Master Endpoint. * - :ex:`currency` - ISO 4217 code of the method. Always equals :ex:`user-currency`. * - :ex:`flow-type` - :ex:`DEPOSIT` or :ex:`PAYOUT` — the direction of the money. * - :ex:`logo-url` - Where to fetch the method logo. Omitted when none is configured. * - :ex:`preferred` - The method the cashier should pre-select. At most one method carries :ex:`true`, and it is always an available one. * - :ex:`available` - Whether the method's own rules let the player use it: maintenance, amount, KYC status, categories. It does not take the top-level :ex:`refusal` into account. * - :ex:`refusal` - Why the method is not available. Omitted when :ex:`available` is :ex:`true`. * - :ex:`min-amount`, :ex:`max-amount` - Amount bounds of the method, both inclusive. Omitted when there is no bound at that end. A payment may still be held to narrower limits decided later (card-specific or routing limits), never to wider ones. * - :ex:`fee` - Reserved. Not filled at the moment: whether a player is charged a fee depends on the routing chosen at payment time. * - :ex:`saved-instruments-only` - The method works only with an instrument the player has already saved. .. note:: An unavailable method is returned with its reason instead of being dropped, so the cashier can show it greyed out and tell the player what to change — for example, the minimum amount. .. _cashier_intent_reading: Reading the Answer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There is one rule: when the answer has a top-level :ex:`refusal`, **the player cannot pay now**, whatever :ex:`methods` and their :ex:`available` flags say. .. list-table:: :widths: 30, 70 :header-rows: 1 :class: longtable * - Answer - What the cashier does * - No :ex:`refusal` - Show the methods. The player may pay with those where :ex:`available` is :ex:`true`. * - :ex:`refusal` and empty :ex:`methods` - The usual refusal. Do not offer a payment; explain the reason from :ref:`Refusal Codes`. When the list became empty during selection, :ex:`refusal` carries the selection reason, e.g. :ex:`NO_METHOD_IN_PLAYER_CURRENCY`, rather than a PAM one. * - :ex:`refusal` and non-empty :ex:`methods` - Possible only on a Master Endpoint configured by support to open the cashier even when PAM refuses or does not answer, and only when the request carried :ex:`currency`. :ex:`refusal` is :ex:`PLAYER_REFUSED_BY_PAM` or :ex:`PAM_UNAVAILABLE`. The methods may be shown for information, but a payment must not be offered on the strength of this answer, even for a method with :ex:`available: true`. A refusal: .. code-block:: json { "type": "intent", "serial-number": "00000000-0000-0000-0000-000002d9b22b", "end-point-id": 221298, "user-categories": [], "methods": [], "saved-methods": [], "refusal": "PLAYER_REFUSED_BY_PAM" } The same player on a Master Endpoint that opens the cashier despite the refusal. The card method is shown as :ex:`available`, but the top-level :ex:`refusal` still means the player cannot pay: .. code-block:: json { "type": "intent", "serial-number": "00000000-0000-0000-0000-000002d9b22c", "end-point-id": 221298, "user-currency": "EUR", "user-categories": [], "methods": [ { "end-point-id": 221296, "identifier": "card", "name": "Card", "currency": "EUR", "flow-type": "DEPOSIT", "preferred": true, "available": true, "min-amount": 1.00, "max-amount": 1000.00, "saved-instruments-only": false } ], "saved-methods": [], "refusal": "PLAYER_REFUSED_BY_PAM" } .. note:: A payment checks the player with PAM again and does not rely on any intent answer. It is declined if the refusal or the outage persists, and may go through once PAM answers normally. When the reason is :ex:`PAM_UNAVAILABLE`, call intent again later instead of sending a payment based on the old answer. .. _cashier_intent_selection: Method Selection ==================================================== Selection runs in two stages. The first decides whether a method is offered at all, the second whether an offered method may be used. **Offered or not.** Auxiliary Endpoints of the Master Endpoint are filtered in this order: #. **Country** — the Auxiliary Endpoint serves the player country (alpha-2 and alpha-3 codes both match). An Auxiliary Endpoint with no countries configured serves every country, and a player whose country is unknown — PAM reported none and the request carried none — passes this filter. #. **Currency** — the Auxiliary Endpoint is in the wallet currency. #. **Direction** — the Auxiliary Endpoint is configured for :ex:`sale` when :ex:`operation=deposit` and for :ex:`payout` when :ex:`operation=payout`. The first filter that leaves nothing names the top-level :ex:`refusal`: :ex:`NO_METHOD_FOR_PLAYER_COUNTRY`, :ex:`NO_METHOD_IN_PLAYER_CURRENCY` or :ex:`NO_METHOD_FOR_OPERATION`. Several Auxiliary Endpoints may carry the same method :ex:`identifier`. Only one of them is returned: the one marked default on the Master Endpoint, otherwise the first in the configured order. **Available or not.** Every offered method is then checked against its own rules, in this order, and the first rule broken is reported in its :ex:`refusal`: #. **Maintenance** — the method is temporarily out of service (:ex:`METHOD_UNDER_MAINTENANCE`). #. **Amount** — when :ex:`amount` was sent, it is within :ex:`min-amount` and :ex:`max-amount` (:ex:`AMOUNT_BELOW_MINIMUM`, :ex:`AMOUNT_ABOVE_MAXIMUM`). #. **KYC status** — when the method is limited to certain KYC statuses, the player holds one of them (:ex:`KYC_STATUS_NOT_ACCEPTED`). Compared case-insensitively. #. **Player category** — when the method is limited to certain categories, the player is in at least one of them (:ex:`PLAYER_CATEGORY_NOT_ACCEPTED`). Compared case-insensitively. .. note:: Country fails open, the player rules fail closed. A missing country lets every method through, because refusing everyone would close the cashier over a field PAM is not obliged to send. A KYC or category rule with nothing to compare against — PAM sent no KYC status or no categories — makes the method unavailable, because the rule was configured on purpose and "unknown" is not "allowed". The pre-selected method (:ex:`preferred`) is the available method marked default on the Master Endpoint, otherwise the first available one. When no method is available, none is pre-selected. .. _cashier_intent_refusals: Refusal Codes ==================================================== The codes are stable and meant for the cashier logic. They are not texts to show to the player: map them to your own wording. .. list-table:: :widths: 30, 15, 55 :header-rows: 1 :class: longtable * - Code - Where - Meaning and what to do * - :ex:`PLAYER_REFUSED_BY_PAM` - Answer - PAM refused the player: an expired session, a blocked or self-excluded player, and similar. Do not offer a payment; the reason is on the Connecting Party side. * - :ex:`PAM_UNAVAILABLE` - Answer - PAM did not answer, or answered something that could not be read. Ask the player to try again later. * - :ex:`PLAYER_CURRENCY_UNKNOWN` - Answer - PAM reported no wallet currency and the request carried no known :ex:`currency`, so there is nothing to select methods by. * - :ex:`NO_METHODS_CONFIGURED` - Answer - The Master Endpoint has no Auxiliary Endpoints. A configuration matter for Neonpay support. * - :ex:`NO_METHOD_FOR_PLAYER_COUNTRY` - Answer - No Auxiliary Endpoint serves the player country. * - :ex:`NO_METHOD_IN_PLAYER_CURRENCY` - Answer - No Auxiliary Endpoint is in the wallet currency. There is no conversion. * - :ex:`NO_METHOD_FOR_OPERATION` - Answer - Auxiliary Endpoints exist for the player, but none is configured for the requested direction. * - :ex:`METHOD_UNDER_MAINTENANCE` - Method - The method is temporarily out of service. Show it greyed out. * - :ex:`AMOUNT_BELOW_MINIMUM` - Method - :ex:`amount` is below :ex:`min-amount`. * - :ex:`AMOUNT_ABOVE_MAXIMUM` - Method - :ex:`amount` is above :ex:`max-amount`. * - :ex:`KYC_STATUS_NOT_ACCEPTED` - Method - The method requires a KYC status the player does not hold, or PAM reported none. * - :ex:`PLAYER_CATEGORY_NOT_ACCEPTED` - Method - The method is limited to player categories the player is not in, or PAM reported none. .. _cashier_intent_errors: Errors ==================================================== A request that cannot be processed at all is answered with an error instead of the JSON above. The error carries :ex:`type` (:ex:`validation-error` or :ex:`error`), :ex:`serial-number`, :ex:`error-message` and :ex:`error-code`. Its format depends on the :ex:`Accept` header. By default the error is form-encoded, with status :code:`200`, like other v4 calls: .. code-block:: http HTTP/1.1 200 Content-Type: text/html;charset=utf-8 type=validation-error &serial-number=00000000-0000-0000-0000-000002d9b22d &error-message=Unknown+operation+%27refill%27%2C+expected+deposit+or+payout &error-code=1 With :ex:`Accept: application/vnd.pay+json` the error is JSON, with status :code:`400` for :ex:`validation-error` and :code:`500` for :ex:`error`: .. code-block:: http HTTP/1.1 400 Content-Type: application/vnd.pay+json;charset=UTF-8 { "type": "validation-error", "serial-number": "00000000-0000-0000-0000-000002d9b22d", "error-message": "Unknown operation 'refill', expected deposit or payout", "error-code": 1 } .. list-table:: :widths: 15, 85 :header-rows: 1 :class: longtable * - Code - Meaning * - :ex:`1` - A required parameter is missing or too long, or :ex:`operation` is neither :ex:`deposit` nor :ex:`payout`. :ex:`error-message` names the parameter. * - :ex:`11` - The settings of the Master Endpoint cannot be read. Check :ex:`MASTERENDPOINTID` in the URL. * - :ex:`20` - PAM integration is not enabled on this Master Endpoint. Nothing is sent to PAM. * - :ex:`2005` - PAM integration is enabled, but its settings are incomplete. A configuration matter for Neonpay support; :ex:`error-message` names the missing setting. A request that fails the signature check is rejected before any of this, see :ref:`OAuth RSA-SHA256`. .. note:: A PAM refusal or outage is not an error: it comes as a regular :code:`200` answer with :ex:`refusal`, see :ref:`Reading the Answer`. .. _cashier_intent_recommendations: Recommendations ==================================================== - Call intent when the cashier opens, separately for :ex:`deposit` and :ex:`payout` if both tabs are shown. - Do not call intent on every keystroke of the amount field. Check the amount against :ex:`min-amount` and :ex:`max-amount` on the client; call intent again with :ex:`amount` only when you need the server to confirm. - Call intent again when the player returns to a cashier left open for a while: the answer reflects the player session at the moment of the call. - Keep the :ex:`serial-number` of every answer in the Connecting Party logs. Without an order it is the only reference support can trace the call by. .. _cashier_intent_next_step: Next Step: Payment ==================================================== Intent only tells which methods may be used; the payment is a separate, regular payment call. It differs from a plain integration in three points. - It is addressed to the :ex:`end-point-id` of the chosen method — the Auxiliary Endpoint — not to the Master Endpoint. - It carries the same :ex:`pam_user_id` and :ex:`pam_session_id` as the intent call. A payment on such an Auxiliary Endpoint without them is declined. - When the Auxiliary Endpoint serves several Master Endpoints for the same direction, the payment also carries :ex:`pam_master_end_point_id` — the Master Endpoint the intent was called on. Without it such a payment is rejected with error :ex:`1`. By direction: .. list-table:: :widths: 20, 30, 50 :header-rows: 1 :class: longtable * - :ex:`operation` - Call - Card data * - :ex:`deposit` - :ref:`/api/v4/sale/` - Card parameters as described for the call, or a :ex:`hosted_fields_token` so that card data never reaches the Connecting Party, see :ref:`Hosted Fields` and the calls that accept the token in its :ref:`Step 3`. * - :ex:`payout` - :ref:`/api/v4/payout/` - Card parameters as described for the call. The payment checks the player with PAM again, independently of the intent answer, so a player refused between opening the cashier and paying is declined at payment time.