.. _partner_api: Merchant-Partner API ==================== Merchant partners can create payments and initiate refunds for their assigned merchants, and read partner-scoped reports. These endpoints use the five HMAC signing headers from :ref:`authentication`, not dashboard JWT authentication. Sign the full path, including ``/external/partner`` for refunds and reporting or ``/external/v1`` for payment creation, and the exact query string. This API is separate from acquisition-partner onboarding. A partner account must be active and the key must have the scope required by the route. Merchant keys cannot access partner reporting routes; partner keys cannot access the merchant-only GET routes under ``/external/v1``. Scopes and Routes ----------------- .. list-table:: :class: partner-routes :widths: 40 30 30 :header-rows: 1 * - Method and path - Required scope - Success response * - ``POST /external/v1/payments/prompt`` - ``payment_prompts:write`` - 201 ``{ prompt, checkoutUrl }`` * - ``POST /external/partner/payments/:id/refund`` - ``payment_prompts:write`` - 201 ``{ refundId }`` * - ``GET /external/partner/overview`` - ``partner_overview:read`` - 200 ``{ partner, metrics, attention, volumeTrend }`` * - ``GET /external/partner/merchants`` - ``partner_merchants:read`` - 200 ``{ data, total, page, limit }`` * - ``GET /external/partner/merchants/:merchantId`` - ``partner_merchants:read`` - 200 merchant details, branches, aggregates and recent payments * - ``GET /external/partner/payments`` - ``partner_payments:read`` - 200 ``{ data, total, page, limit }`` * - ``GET /external/partner/payment-filters`` - ``partner_payments:read`` - 200 ``{ merchants: [{ id, name }], blockchainIds }`` * - ``GET /external/partner/payments/:paymentId`` - ``partner_payments:read`` - 200 partner payment reporting row * - ``GET /external/partner/team`` - ``partner_team:read`` - 200 ``{ data }`` Partner developer keys are currently restricted to ``partner_overview:read``, even if other scopes are stored on the key. Partner owner keys can use the partner scope set above. The application rate limiter and structured :doc:`errors ` also apply to these routes. Create on Behalf of a Merchant ------------------------------ Use the :ref:`external_payments_api` create contract, adding ``merchantId``. The ID must be a UUID for an active merchant assigned to your partner. Missing ``merchantId`` returns E1001; an unowned merchant returns E3001. The target merchant's default Main branch, onboarding, network configuration and redirect allowlist apply. .. code-block:: typescript // Use partner credentials with the signed-fetch helper. // Load a persisted key for this intended payment; reuse on retries. const response = await miraclePayFetch( "POST", "/external/v1/payments/prompt", { merchantId: "550e8400-e29b-41d4-a716-446655440000", amount: 1099, // $10.99 in cents customerReferenceId: "order_12345", }, { "Idempotency-Key": idempotencyKey }, ); if (!response.ok) { const { error } = await response.json(); throw new Error(`${error.errorCode}: ${error.message}`); } const { prompt, checkoutUrl } = await response.json(); The response uses the same cents-based payment object as merchant creation. To read it afterwards using partner credentials, use ``GET /external/partner/payments/:paymentId`` and its reporting contract below. A linked merchant can instead use its own key only when its partner has enabled self-service API payments. Otherwise merchant payment routes return E2103. That setting does not replace ownership checks on partner-key creation. .. _partner_api_refunds: Refund on Behalf of a Merchant ------------------------------ ``POST /external/partner/payments/:id/refund`` starts the same full-refund flow as the merchant dashboard. The path ``id`` is the original payment UUID. This endpoint is under the partner namespace, not ``/external/v1``. Use a merchant-partner owner key with ``payment_prompts:write`` and provide the target ``merchantId`` in the body. The server verifies both that the active merchant belongs to the authenticated partner and that the payment belongs to that merchant through its branch. Another partner's merchant, a mismatched payment, or a payment without a branch cannot be refunded. Partner developer keys cannot use this route. .. code-block:: typescript const response = await miraclePayFetch( "POST", `/external/partner/payments/${paymentId}/refund`, { merchantId: "550e8400-e29b-41d4-a716-446655440000" }, ); if (!response.ok) { const { error } = await response.json(); throw new Error(`${error.errorCode}: ${error.message}`); } const { refundId } = await response.json(); // Store this ID for reconciliation. The refund is not yet paid out. Access and eligibility ^^^^^^^^^^^^^^^^^^^^^^ * A MiraclePay administrator must enable **API refunds** on the partner's create/edit form. This partner-level permission (``apiRefundEnabled``) defaults to disabled and allows the partner to refund its linked merchants' payments via API. Disabled access returns ``partner_api_refund_disabled`` (E2104, HTTP 403). * Merchant-admin keys may also call this same endpoint for their own payments, sending ``{}`` instead of ``merchantId``. A partner-linked merchant needs both its partner's API refunds permission and the existing self-service API payment permission; the latter returns E2103 when disabled. A merchant without a partner is not subject to the partner toggles. * Toggle changes apply to existing keys on subsequent requests. Turning API refunds off blocks new API initiation, not merchant-dashboard refunds or the continuation of existing refunds. It does not disable payment creation or cancellation. * ZeroHash must be enabled globally and active for the target merchant. The original payment must be a successful ZeroHash payment, have a positive refundable amount and a shopper email on file, and must not already have a refund. Request and result ^^^^^^^^^^^^^^^^^^ Only ``merchantId`` is accepted in the JSON body, and only for partner keys. It must be a UUID. Do not send ``amount``, destination wallet or other fields: partial refunds are not supported. The five signing headers, exact-body signature, nonce rules and structured errors are the same as payment creation. HTTP 201 returns ``{ "refundId": "" }``. The server creates a separate pending refund record, links it to the original payment, and attempts to email the shopper a claim link. Email delivery is best-effort; a successful response does not guarantee delivery or payout. The original payment remains ``successful``. Do not treat this response or the original payment status as confirmation that funds were returned. Subscribe the partner's webhook endpoint to ``refund.initiated`` for creation and ``refund.completed`` for confirmed settlement. The merchant may independently subscribe its own endpoint to either event; existing subscriptions are not automatically extended. Match ``data.object.id`` to the returned ``refundId`` and ``data.object.paymentPromptId`` to the original payment. See :ref:`refund_webhooks` for the payload, partner merchant identity, delivery ordering and recovery limits. Unlike payment creation, this route does not provide ``Idempotency-Key`` response replay. Repeated or concurrent initiation returns HTTP 409 once a refund exists. After an uncertain response, reconcile the existing refund through the merchant dashboard or support rather than interpreting a conflict as payout confirmation. Refund-specific errors ^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :widths: 15 35 50 :header-rows: 1 * - HTTP - Code - Meaning * - 400 - ``parameter_invalid`` (E1001) - Invalid ID/body, missing partner ``merchantId``, merchant key supplying ``merchantId``, or failed refund eligibility such as a non-successful payment or missing shopper email. * - 400 - ``parameter_unknown`` (E1002) - An unsupported body field, such as ``amount`` or a destination wallet. * - 400 - ``merchant_not_configured`` (E1030) - ZeroHash is disabled globally or inactive for the selected merchant. * - 403 - ``partner_api_refund_disabled`` (E2104) - The partner's API refunds permission is disabled. * - 404 - ``resource_missing`` (E3001) - Unowned/missing merchant or payment, branchless payment, non-ZeroHash payment, or a refund record supplied as the original payment. * - 409 - ``conflict`` (E1040) - A refund already exists or another request won the concurrent initiation. Standard authentication, onboarding, scope and rate-limit errors also apply; see :doc:`/errors`. Reporting Queries ----------------- Both merchant and payment lists use ``page`` (minimum 1, default 1) and ``limit`` (1–100, default 20), not merchant payment cursors. Responses contain ``data``, ``total``, ``page`` and ``limit``. Lists are ordered newest-first by creation time. Merchant list accepts ``search`` (name, email or ID, at most 255 characters) and ``actionRequired=true`` to restrict results to merchants awaiting verification. Partner payment list accepts: .. list-table:: :widths: 26 74 :header-rows: 1 * - Parameter - Meaning * - ``search`` - Partial match across merchant name/ID, payment ID, address, transaction hash and customer reference. * - ``status`` - ``pending``, ``successful``, ``unsuccessful``, ``expired`` or ``cancelled``. * - ``transactionId`` - Partial payment ID, not a blockchain transaction hash. * - ``merchantId`` - Exact merchant UUID. * - ``merchantName`` - Partial merchant name. * - ``addressSearch`` - Partial deposit address. * - ``txHash`` - Partial transaction hash. * - ``blockchainId`` - Supported blockchain ID. * - ``startDate``, ``endDate`` - ISO date/time bounds on creation time, inclusive. Text filters have a 255-character maximum. ``actionRequired`` is inherited by the payment query DTO but does not filter payment results. Detail path IDs must be UUIDs; unowned or missing details return E3001. Reporting Response Differences ------------------------------ .. warning:: Partner reporting is not the canonical merchant payment DTO. Payment reporting ``amount`` and ``commissionAmount`` are database decimal USD values in **dollars**, not integer cents. Do not reuse the merchant DTO's money decoder. Partner overview and merchant aggregate volumes also use dollars. Payment creation still takes and returns cents. Partner payment list rows and payment detail contain these fields: .. code-block:: text id, amount, commissionAmount, tokenAmount, status, sessionStatus, flow, paymentSystem, blockchainId, address, transactionHash, customerReferenceId, note, createdAt, expiresAt, paidAt, branchId, branchName, merchantId, merchantName There is no ``object: "payment_prompt"`` discriminator on these reporting rows. They expose reporting fields such as ``paymentSystem`` and ``address`` that merchant payment responses deliberately omit. Do not assume ``settlementModel``, ``lastError`` or ``cancellationReason`` are present. Overview metrics include ``totalMerchants``, ``approvedMerchants``, ``actionRequired``, ``volume30d``, ``totalVolume`` and ``totalPayments``. Merchant detail includes branches, merchant payment aggregates and up to ten recent payment entities, rather than the canonical merchant DTO. ``payment-filters`` returns assigned merchant choices and the non-null blockchain IDs present in the partner's payment history; it is not an active-network discovery endpoint.