# MD for: https://www.mercadopago.com.ar/developers/es/docs/qr-code-ca/migrate-instore-orders-to-orders.md \# How to migrate from the Instore Orders API to the Orders API The Orders API unifies QR Code payment processing on Mercado Pago, providing standardized endpoints, a consolidated status model, and new native capabilities that did not exist in the Instore Orders API. In addition, all new Mercado Pago features will be developed on the Orders API. Migrating from the Instore Orders API to the Orders API involves \*\*updating endpoints and request fields\*\*, \*\*consolidating the notifications model\*\*, and leveraging \*\*new native resources\*\* that did not exist before: dedicated query endpoints by \`order\_id\`, cancellation, and refund. The migration does not change the business flow: the customer continues scanning the QR Code with the Mercado Pago app and confirming the payment. In addition, in this modality, the Orders API enables cash withdrawal (\*extracash\* and \*cash-out\*), payment method discounts, category discounts, and interest-free installments. For more details, see the \[transaction processing documentation\](https://www.mercadopago.com.ar/developers/en/docs/qr-code-ca/payment-processing). Read on to learn how to complete this migration. ## Map the endpoint changes Before starting the migration steps, see the table below for an overview of all endpoint changes. In the Instore Orders API, each operation used a URL with the user and point-of-sale identifier in the path. The Orders API consolidates these operations into standardized endpoints and introduces resources that did not exist before. | Operation | Instore Orders API | Orders API | |---|---|---| | Create order | :TagComponent{tag="API" text="POST /mpmobile/instore/qr/{user\_id}/{external\_id}" href="/developers/en/reference/instore\_orders/\_mpmobile\_instore\_qr\_user\_id\_external\_id/post"} | :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"} | | Get order | No dedicated endpoint existed | :TagComponent{tag="API" text="GET /v1/orders/{order\_id}" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/get-order/get"} | | Cancel order | :TagComponent{tag="API" text="DELETE /mpmobile/instore/qr/{user\_id}/{external\_id}" href="/developers/en/reference/instore\_orders/\_mpmobile\_instore\_qr\_user\_id\_external\_id/delete"} | :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/cancel" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/cancel-order/post"} | | Refund order | Via Payments API (no dedicated endpoint in the Instore Orders API) | :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/refund" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/refund-order/post"} | In the Instore Orders API, there were two valid endpoints for creation and cancellation: :TagComponent{tag="API" text="POST /mpmobile/instore/qr/{user\_id}/{external\_id}" href="/developers/en/reference/instore\_orders/\_mpmobile\_instore\_qr\_user\_id\_external\_id/post"}, using the external point-of-sale identifier, and \`/mpmobile/instore/qr/{pos\_id}\`, using the internal identifier. Both migrate to the same endpoint in the Orders API: :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"}, where the point of sale is identified by the \`config.qr.external\_pos\_id\` field in the body. ## Update the status and notifications model One of the most significant changes between the Instore Orders API and the Orders API is the \`status\` model. In the Instore Orders API, the state of a transaction was determined by monitoring notifications from two independent topics , \`payments\` and \`merchant\_orders\`, with distinct fields and values for each. The Orders API unifies this behavior into a single \`status\` field in the order. See the complete mapping below. In addition, the Orders API also introduces the \`status\_detail\` field, which provides greater detail on the transaction state. The possible values are \`created\`, \`canceled\`, \`accredited\`, \`refunded\`, and \`expired\`. :::AccordionComponent{title="Map status values" pill="1"} In the Instore Orders API, the \`merchant\_order\` is created when the order is generated, so \`merchant\_orders\` topic notifications are available from the start of the flow. The \`payment\` object, on the other hand, is only created when a payment attempt exists, whether approved or rejected. Integrations monitoring only the \`payments\` topic received no notification until the user started the payment process. In the Orders API, this behavior is unified: the \`status\` field of the order reflects the complete state from creation, without the need to monitor two independent topics. | \`payments\` topic (\`status\`) | \`merchant\_orders\` topic (\`status\` / \`order\_status\`) | Orders API (\`status\`) | Note | |---|---|---|---| | — | \`opened\` (no payment or rejected payment) | \`created\` | In the Instore Orders API, \`opened\` indicated that the order did not yet have an approved payment. In the Orders API, this state is explicit from creation. | | \`approved\` | \`closed\` + \`order\_status: "paid"\` | \`processed\` | Both topics converged on the same result: approved payment. In the Orders API, the state is self-contained. | | \`rejected\` | \`opened\` (the \`merchant\_order\` does not change state and remains open) | No equivalent | In the Instore Orders API, a rejected payment left the \`merchant\_order\` in \`opened\` awaiting a new attempt. In the Orders API, rejected payments are not exposed and the state remains \`created\` until an approved payment is received. | | \`refunded\` | \`closed\` + \`order\_status: "reverted"\` | \`refunded\` | Full refund. In the \`merchant\_orders\` topic, identifiable by \`order\_status: "reverted"\`. | | — | — | \`canceled\` | New status with no equivalent in the Instore Orders API. | | — | — | \`expired\` | New status with no equivalent in the Instore Orders API. | ::: Regarding notifications, in the Legacy API they could be configured via the \`notification\_url\` field in the request body (IPN model) or via webhooks with the \`payments\` and \`merchant\_orders\` topics. In the Orders API, the \`notification\_url\` field does not exist and the \`payments\` and \`merchant\_orders\` topics are not compatible. Configure notifications in \[Your integrations\](https://www.mercadopago.com.ar/developers/panel/app), subscribing to the \*\*"Order (Mercado Pago)"\*\* (\`orders\`) topic, before going to production. For more information, see the \[notifications documentation\](https://www.mercadopago.com.ar/developers/en/docs/qr-code-ca/notifications). ## Adapt the headers The Orders API introduces two mandatory header changes that affect all endpoints. The Access Token must be sent via the \`Authorization\` header. Sending it as a query parameter is not allowed in the Orders API. Apply the following changes before testing any other resource. :::AccordionComponent{title="Remove the X-Ttl-Store-Preference header" pill="1"} The \`X-Ttl-Store-Preference\` header was used in the Instore Orders API to define the order validity period in seconds. In the Orders API, this functionality was replaced by the \`expiration\_time\` field in the request body, in ISO 8601 format, for example \`PT5M\` for 5 minutes. The default value is \`PT10M\`. Values greater than 10 minutes are ignored and the order expires in 10 minutes. Remove this header from all requests and migrate the validity control to the \`expiration\_time\` field. ::: :::AccordionComponent{title="Add the X-Idempotency-Key header" pill="2"} The \`X-Idempotency-Key\` header is required for order creation, cancellation, and refund operations. It ensures that a repeated request with the same key returns the original result without processing the operation again. Send a unique UUID v4 or random string per request. \*\*GET\*\* operations do not require this header. > WARNING > > If the same \`X-Idempotency-Key\` is reused with a different body, the API will return the \`idempotency\_key\_already\_used\` error. Generate a new key for each distinct operation. ::: ## Migrate order creation The creation endpoints :TagComponent{tag="API" text="POST /mpmobile/instore/qr/{user\_id}/{external\_id}" href="/developers/en/reference/instore\_orders/\_mpmobile\_instore\_qr\_user\_id\_external\_id/post"} and \`POST /mpmobile/instore/qr/{pos\_id}\` migrate to :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"}. Beyond the URL change, the request structure was significantly reorganized: the user and point-of-sale identifiers move from the path to the body, new required fields were introduced, and the Orders API offers three QR Code modes configurable via \`config.qr.mode\`. Follow the steps below to adapt the request, response, and error handling. \`\`\`curl curl -X POST \\ 'https://api.mercadopago.com/v1/orders' \\ -H 'Content-Type: application/json' \\ -H 'X-Idempotency-Key: {{IDEMPOTENCY\_KEY}}' \\ -H 'Authorization: Bearer {{ACCESS\_TOKEN}}' \\ -d '{ "type": "qr", "total\_amount": "50.00", "external\_reference": "ext\_ref\_1234", "config": { "qr": { "external\_pos\_id": "STORE001POS001", "mode": "static" } }, "transactions": { "payments": \[ { "amount": "50.00" } \] }, "items": \[ { "title": "Smartphone", "unit\_price": "50.00", "quantity": 1, "unit\_measure": "unit" } \] }' \`\`\` :::::AccordionComponent{title="Map the request body fields" pill="1"} In the Orders API, the user identity is obtained directly from the Access Token. The \`{user\_id}\` is no longer a path parameter. The point of sale is now identified by the \`config.qr.external\_pos\_id\` field in the body. The \`type\` field (with value \`"qr"\`) and \`external\_reference\` become required. The table below shows the fields that existed in both APIs and were updated during the migration. | Instore Orders API | Orders API | Description | Change | |---|---|---|---| | \`{user\_id}\` (path param) | Does not exist | Mercado Pago user identifier. In the Instore Orders API, it was sent in the path. In the Orders API, identity is obtained directly from the Access Token. | No longer a path parameter. | | \`{external\_id}\` (path param) | \`config.qr.external\_pos\_id\` | External point-of-sale identifier, defined by the integrator. In the Instore Orders API, it was sent in the path. In the Orders API, it is sent in the body inside \`config.qr\`. | Moves from path parameter to body. | | \`external\_reference\` | \`external\_reference\` | Reference that can synchronize with the integrator's sales system. In the Orders API, it is required, with a maximum of 64 characters, only letters, numbers, \`-\` and \`\_\`. Cannot contain PII data. | Becomes \*\*required\*\*. | | \`notification\_url\` | Does not exist | URL to receive payment or merchant\_order notifications. In the Orders API, notifications are configured as a webhook in your application within Your integrations by selecting the \*\*"Order (Mercado Pago)"\*\* topic. For more information, see the \[notifications documentation\](https://www.mercadopago.com.ar/developers/en/docs/qr-code/notifications). | Removed from the body. | | \`sponsor\_id\` | \`integration\_data.sponsor.id\` | \`USER\_ID\` of the Mercado Pago account of the integrating system. | Moves to the \`integration\_data.sponsor\` node. | | \`items\[\].id\` | \`items\[\].external\_code\` | Item identifier in the external system. | Renamed to \`items\[\].external\_code\`. | | \`items\[\].title\` | \`items\[\].title\` | Item name. In the Orders API, max. 150 characters. | Becomes \*\*required\*\*. | | \`items\[\].currency\_id\` | Does not exist | Item currency identifier in ISO 4217 format. In the Orders API, the currency is determined from the seller's account. | Removed. | | \`items\[\].unit\_price\` | \`items\[\].unit\_price\` | Item unit price. In the Instore Orders API, it was a \`number\`. In the Orders API, it is a decimal \`string\`. Accepts two decimal places (e.g.: \`"15.00"\`) or none. | Changes from \`number\` to decimal \`string\` and becomes \*\*required\*\*. | | \`items\[\].quantity\` | \`items\[\].quantity\` | Item quantity. In the Instore Orders API, it was a \`number\`. In the Orders API, it is an \`integer\`. | Changes from \`number\` to \`integer\` and becomes \*\*required\*\*. | | \`items\[\].description\` | \`description\` | Item description or order reason. In the Orders API, the description exists only at the root level of the order, not inside \`items\`. Max. 150 characters. Must not contain PII data. | Moves from item level to order root level. | | \`items\[\].picture\_url\` | Does not exist | Item image URL. | Removed. Has no equivalent in the Orders API. | | \`marketplace\_fee\` | \`marketplace\_fee\` | Marketplace fee amount. In the Instore Orders API, it was a \`number\`. In the Orders API, it is a decimal \`string\`. Accepts two decimal places (e.g.: \`"15.00"\`) or none. Exclusive to OAuth integrations. | Changes from \`number\` to decimal \`string\`. | | \`X-Ttl-Store-Preference\` (header) | \`expiration\_time\` | Order validity period. In the Instore Orders API, it was defined in seconds via header. In the Orders API, it is defined in ISO 8601 format (e.g.: \`PT5M\` for 5 minutes) in the body. Minimum: 30 seconds. Maximum: 3600 hours. For \`static\` mode, the default value is \`PT10M\`. Values greater than 10 minutes are ignored and the order expires in 10 minutes. | Changes from a header in seconds to a body field in ISO 8601 format. | The Orders API also introduces the following fields with no equivalent in the Instore Orders API. | Field | Description | |---|---| | \`type\` | Order type. For QR Code payments, the only possible value is \`"qr"\`. \*\*Required.\*\* | | \`total\_amount\` | Total order amount. Represents the sum of transactions. If there is a cash withdrawal, it must be the sum of the payment amount and the withdrawal amount. Accepts two decimal places (e.g.: \`"15.00"\`) or none. | | \`config.qr.mode\` | QR Code mode. The possible values are \`static\` (static QR Code associated with the point of sale, equivalent to the behavior of the Instore Orders API), \`dynamic\` (unique QR Code per transaction, returned in \`type\_response.qr\_data\`), \`hybrid\` (both modes in parallel). The default value is \`static\`. | | \`transactions.payments\[\].amount\` | Payment amount inside the \`transactions\` node. Accepts two decimal places (e.g.: \`"15.00"\`) or none. Only 1 payment transaction per order. | | \`items\[\].unit\_measure\` | Item unit of measure. Max. 10 characters. \*\*Required.\*\* | | \`items\[\].external\_categories\[\].id\` | Item category identifier in the external system. Enables category-based discounts. Cannot be used together with \`discounts\`. | | \`discounts.payment\_methods\[\].new\_total\_amount\` | New total order amount when a discount is applied. Accepts two decimal places (e.g.: \`"15.00"\`) or none. Cannot be used together with \`items\[\].external\_categories\`. | | \`discounts.payment\_methods\[\].type\` | Payment method to which the discount applies. The possible values are \`debit\_card\`, \`credit\_card\`, \`account\_money\`, \`prepaid\_card\`. Up to 4 methods can be defined. | | \`integration\_data.platform\_id\` | Platform identifier, assigned by Mercado Pago. | | \`integration\_data.integrator\_id\` | Integrator identifier, assigned by Mercado Pago. Must have the \`dev\_\` prefix. | > SUCCESS\_MESSAGE > > Check all available parameters in the :TagComponent{tag="API" text="API Reference" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"}. ::::: :::AccordionComponent{title="Adapt the creation response fields" pill="2"} The table below shows the creation response fields that existed in both APIs and were updated during the migration. | Instore Orders API | Orders API | Description | Change | |---|---|---|---| | \`id\` | \`id\` | Identifier of the created order. In the Instore Orders API, the format was \`{collector\_id}-{uuid}\`. In the Orders API, it is a proprietary alphanumeric ID (e.g.: \`ORD00001111222233334444555566\`). | ID format changes from \`{collector\_id}-{uuid}\` to alphanumeric. | | \`collector\_id\` | \`user\_id\` | Seller identifier. In the Instore Orders API, it was an \`integer\`. In the Orders API, it is a \`string\`. | Renamed from \`collector\_id\` to \`user\_id\`. Changes from \`integer\` to \`string\`. | | \`collector\` | Does not exist | Seller data. | Removed. Seller data is not returned in the order. | | \`total\_amount\` | \`total\_amount\` | Total order amount. In the Instore Orders API, it was a \`number\`. In the Orders API, it is a decimal \`string\`. | Changes from \`number\` to decimal \`string\`. | | \`amount\` | Does not exist | Order amount. Was always equal to \`total\_amount\`. | Removed. In the Orders API, the payment amount is in \`transactions.payments\[\].amount\`. | | \`external\_reference\` | \`external\_reference\` | External order reference. | No changes. | | \`notification\_url\` | Does not exist | URL configured to receive notifications. | Removed from the response. Configure webhook notifications in your application within Your integrations. For more information, see the \[notifications documentation\](https://www.mercadopago.com.ar/developers/en/docs/qr-code/notifications). | | \`expiration\_date\_to\` | Does not exist | Order expiration date and time in absolute ISO 8601 format. | Removed. In the Orders API, expiration is configured as a relative duration in \`expiration\_time\`. | | \`expires\` | Does not exist | Indicates whether the order has an expiration date. | Removed. In the Orders API, the order always expires. | | \`marketplace\_fee\` | \`marketplace\_fee\` | Marketplace fee. In the Instore Orders API, it was a \`number\`. In the Orders API, it is a decimal \`string\`. | Changes from \`number\` to decimal \`string\`. | | \`sponsor\_id\` | \`integration\_data.sponsor.id\` | \`USER\_ID\` of the integrating system. In the Instore Orders API, it was an \`integer\`. In the Orders API, it is a \`string\`. | Moves to \`integration\_data.sponsor\`. Changes from \`integer\` to \`string\`. | | \`site\_id\` | \`country\_code\` | Site/country identifier. In the Instore Orders API, the format was lowercase (e.g.: \`"mla"\`). In the Orders API, it is the country code in uppercase (e.g.: \`"AR"\`). | Renamed from \`site\_id\` to \`country\_code\`. Format changes from lowercase to uppercase. | | \`items\[\].description\` | \`description\` | Item description. | Moves from item level to order root level. | | \`items\[\].id\` | \`items\[\].external\_code\` | Item identifier in the external system. | Renamed to \`items\[\].external\_code\`. | In the migration to the Orders API, the following fields were \*\*removed\*\* and have no direct equivalent. | Field | Note | |---|---| | \`operation\_type\` | Removed. Has no equivalent in the Orders API. | | \`payment\_methods\` | Removed. Payment method exclusions are not returned at the order level. | | \`payment\_methods.included\_payment\_types\` | Removed along with the \`payment\_methods\` node. | | \`marketplace\` | Removed. Has no equivalent in the Orders API. | | \`back\_urls\` | Removed. Has no equivalent in the Orders API. | | \`payer\` | Removed. Payer data is not returned in the creation response. | | \`client\_id\` | Removed. Redundant with \`user\_id\`. | | \`processing\_modes\` | Removed. Replaced by the \`processing\_mode\` field (string) in the Orders API. | | \`internal\_metadata\` | Removed. Internal-use field with no equivalent in the Orders API. | | \`items\[\].currency\_id\` | Removed. The currency is exposed at the order level in the \`currency\` field. | | \`items\[\].picture\_url\` | Removed. Has no equivalent in the Orders API. | The Orders API also introduces the following fields in the creation response. | Field | Description | |---|---| | \`type\` | Order type. For QR Code: always \`qr\`. | | \`description\` | Product or service description, order reason (in Legacy it was \`items\[\].description\`). | | \`expiration\_time\` | Order expiration time in ISO 8601 format. | | \`processing\_mode\` | Processing mode. For QR Code: always \`automatic\`. | | \`status\` | Current order status. When created it is \`created\`. | | \`status\_detail\` | Current order status detail. The possible values are \`created\`, \`canceled\`, \`accredited\`, \`refunded\`, \`expired\`. | | \`currency\` | Currency identifier. The possible values are \`ARS\`, \`BRL\`, \`CLP\`, \`UYU\`. | | \`created\_date\` | Order creation date. Format \`yyyy-MM-ddTHH:mm:ss.sssZ\`. | | \`last\_updated\_date\` | Order last update date. Format \`yyyy-MM-ddTHH:mm:ss.sssZ\`. | | \`config.qr.external\_pos\_id\` | External point-of-sale identifier associated with the order. | | \`config.qr.mode\` | Configured QR Code mode. The possible values are \`static\`, \`dynamic\`, \`hybrid\`. For integrations migrated from the Instore Orders API, the value is \`static\`. | | \`transactions.payments\[\].id\` | Payment transaction identifier. | | \`transactions.payments\[\].amount\` | Payment amount. | | \`transactions.payments\[\].status\` | Payment status. When created it is \`created\`. | | \`transactions.payments\[\].status\_detail\` | Payment status detail. When created it is \`ready\_to\_process\`. | | \`type\_response.qr\_data\` | QR Code data string that must be converted into a QR Code for the payment to be made. Present only when \`config.qr.mode\` is \`dynamic\` or \`hybrid\`. | | \`integration\_data.application\_id\` | Mercado Pago application identifier. | | \`items\[\].title\` | Item name. | | \`items\[\].unit\_price\` | Item unit price. | | \`items\[\].quantity\` | Item quantity. | | \`items\[\].unit\_measure\` | Item unit of measure. | | \`items\[\].external\_categories\[\].id\` | Item category identifier in the external system. | | \`discounts.payment\_methods\[\].new\_total\_amount\` | New total order amount when a discount is applied. | | \`discounts.payment\_methods\[\].type\` | Payment method to which the discount applies. | ::: :::AccordionComponent{title="Update error handling in creation" pill="3"} The Orders API consolidates most field-specific validation errors into the generic codes \`property\_value\` and \`property\_type\`, with the field detail in the response body. Response messages are also more descriptive, making it easier to identify and resolve issues. See the tables below for more details. ### Errors that disappear The following errors exist in the Instore Orders API but \*\*have no equivalent\*\* in the Orders API. | HTTP | Instore Orders API | Note | |---|---|---| | \`400\` | \`invalid\_collector\_id\` | Removed. Identity is obtained directly from the Access Token. | ### Renamed errors The following errors were renamed in the Orders API. | HTTP | Instore Orders API | Orders API | Note | |---|---|---|---| | \`400\` | \`invalid\_sponsor\_id\` | \`sponsor\_id\_not\_valid\` | The field migrates to \`integration\_data.sponsor.id\`. | | \`404\` | \`pos\_obtainment\_error\` | \`pos\_not\_found\` | The \`external\_pos\_id\` moves from path parameter to body. | ### Errors that change behavior The following errors exist in both APIs but with different behavior in the Orders API. | HTTP | Instore Orders API | Orders API | Note | |---|---|---|---| | \`400\` | \`invalid\_items\` | \`property\_value\` | \`property\_value\` consolidates field-level validation errors. The field with the error is indicated in the response detail. | | \`400\` | \`json\_syntax\_error\` | \`property\_type\` | Closest equivalent. In the Instore Orders API, it also covered malformed JSON in general. | | \`400\` | \`invalid\_access\_token\` | \`unauthorized\` (\*\*HTTP 401\*\*) | In the Instore Orders API, it was \`400\`. In the Orders API, it is \`401\`. | ### Errors that remain unchanged The following error has the same behavior in both APIs. | HTTP | Error | Note | |---|---|---| | \`500\` | Generic | Internal error. Check the response and retry the request. | ### Errors introduced by the Orders API The following errors have no equivalent in the Instore Orders API. | HTTP | Error | Note | |---|---|---| | \`400\` | \`empty\_required\_header\` | \`X-Idempotency-Key\` missing. | | \`400\` | \`unsupported\_site\` | Attempt to create the order in an unsupported country. | | \`400\` | \`unsupported\_properties\` | Unsupported property in the body. The field with the error is indicated in the response detail. | | \`400\` | \`bad\_request\` | Unsupported or invalid fields in general. | | \`400\` | \`marketplace\_not\_valid\` | The Access Token was not obtained via OAuth and a valid marketplace cannot be identified. | | \`400\` | \`seller\_configuration\` | The seller is not authorized to perform cash withdrawal transactions. Contact the Mercado Pago commercial team to enable this feature. | | \`401\` | \`unauthorized\` | Invalid or expired token. The equivalent in the Instore Orders API was \`invalid\_access\_token\` with code \`400\`. | | \`404\` | \`marketplace\_fee\_not\_allowed\` | Sending \`marketplace\_fee\` is not allowed because the marketplace was not found. | | \`409\` | \`idempotency\_key\_already\_used\` | The \`X-Idempotency-Key\` was already used with a different request in the last 24 hours. | ::: ## Implement order queries The query endpoint :TagComponent{tag="API" text="GET /v1/orders/{order\_id}" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/get-order/get"} is a capability exclusive to the Orders API. The Instore Orders API did not have a dedicated endpoint to query the status of a transaction. Previously, the status was obtained exclusively via webhook notifications from the \`payments\` and \`merchant\_orders\` topics. The Orders API allows querying the complete order status at any time, including status, payments, refunds, and payment method data. \`\`\`curl curl -X GET \\ 'https://api.mercadopago.com/v1/orders/{{ORDER\_ID}}' \\ -H 'Authorization: Bearer {{ACCESS\_TOKEN}}' \`\`\` :::AccordionComponent{title="Adapt the query response fields" pill="1"} The \*\*GET\*\* response includes all fields from the creation response, plus the following additional fields available after payment processing. | Field | Description | |---|---| | \`integration\_data.platform\_id\` | Platform identifier, assigned by Mercado Pago. | | \`integration\_data.integrator\_id\` | Integrator identifier, assigned by Mercado Pago. | | \`integration\_data.sponsor.id\` | \`USER\_ID\` of the integrating system. | | \`transactions.payments\[\].refunded\_amount\` | Refunded amount. Present only when a refund occurred. | | \`transactions.payments\[\].paid\_amount\` | Total amount paid. If the payment was made with a discount, reflects that amount. | | \`transactions.payments\[\].status\` | Current payment status. The possible values are \`created\`, \`canceled\`, \`processed\`, \`refunded\`, \`expired\`. | | \`transactions.payments\[\].status\_detail\` | Detailed payment status. The possible values are \`created\`, \`canceled\_by\_api\`, \`accredited\`, \`refunded\`, \`expired\`, \`in\_review\`. | | \`transactions.payments\[\].reference\_id\` | Payment identifier associated with the order. | | \`transactions.payments\[\].payment\_method.type\` | Payment method selected for the transaction. | | \`transactions.payments\[\].payment\_method.installments\` | Number of installments selected. | | \`transactions.payments\[\].payment\_method.id\` | Payment method or card brand identifier. | | \`transactions.payments\[\].discounts.type\` | Payment method with which the discount was applied. Present only if a discount was applied. | | \`transactions.refunds\[\].id\` | Refund transaction identifier. | | \`transactions.refunds\[\].transaction\_id\` | Identifier of the payment being refunded. | | \`transactions.refunds\[\].reference\_id\` | Identifier linking the transaction to its refund. | | \`transactions.refunds\[\].amount\` | Amount returned in the refund. | | \`transactions.refunds\[\].status\` | Refund status. The possible values are \`processing\`, \`processed\`, \`failed\`. | | \`items\[\].title\` | Item name. | | \`items\[\].unit\_price\` | Item unit price. | | \`items\[\].quantity\` | Item quantity. | | \`items\[\].unit\_measure\` | Item unit of measure. | | \`items\[\].external\_code\` | External item code. | | \`items\[\].external\_categories\[\].id\` | Item category identifier. | | \`discounts.payment\_methods\[\].new\_total\_amount\` | New total amount when a discount is applied. | | \`discounts.payment\_methods\[\].type\` | Payment method to which the discount applies. | ::: :::AccordionComponent{title="Update error handling in query" pill="2"} Since this is a new endpoint in the Orders API, there is no equivalent in the Instore Orders API. The errors documented below \*\*are exclusive to the new API\*\*. | HTTP | Error | Note | |---|---|---| | \`400\` | \`invalid\_path\_param\` | The \`order\_id\` sent to query the order has an invalid format. It must start with the \`ORD\` prefix followed by 26 characters. | | \`401\` | \`unauthorized\` | Invalid or expired Access Token. | | \`404\` | \`order\_not\_found\` | The \`order\_id\` does not correspond to any created order. | | \`500\` | Generic | Internal error. Check the response and retry the request. | ::: ## Update order cancellation The cancellation endpoint changes completely in structure: from :TagComponent{tag="API" text="DELETE /mpmobile/instore/qr/{user\_id}/{external\_id}" href="/developers/en/reference/instore\_orders/\_mpmobile\_instore\_qr\_user\_id\_external\_id/delete"} to :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/cancel" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/cancel-order/post"}. The HTTP method changes from \`DELETE\` to \`POST\`, the \`{user\_id}\` and \`{external\_id}\` are removed from the path, and the cancellation now operates on the unique order identifier. In the Instore Orders API, cancellation deleted the active order associated with the point of sale, an operation on the point of sale rather than on the individual transaction. In the Orders API, cancellation operates directly on the order via \`order\_id\`, without depending on the point of sale in the path. The response changed from empty (HTTP \`204\`) to the complete order object with \`status: "canceled"\` (HTTP \`200\`). :::AccordionComponent{title="Adapt cancellation headers" pill="1"} The header required for this operation is: | Header | Required | Description | |---|---|---| | \`X-Idempotency-Key\` | Required | Unique key per request. | The response returns the complete order object with \`status: "canceled"\`. In the Instore Orders API, cancellation returned HTTP \`204\` with no body. The table below lists the fields returned in the cancellation response. | Field | Description | |---|---| | \`id\` | Identifier of the canceled order, generated by Mercado Pago. | | \`user\_id\` | Identifier of the Mercado Pago user who created the order. | | \`type\` | Order type. For QR Code: always \`qr\`. | | \`external\_reference\` | External order reference, assigned at creation. | | \`description\` | Product or service description, order reason. | | \`expiration\_time\` | Order expiration time in ISO 8601 format. | | \`processing\_mode\` | Processing mode. For QR Code: always \`automatic\`. | | \`total\_amount\` | Total order amount. | | \`country\_code\` | Mercado Pago application site/country identifier. | | \`marketplace\_fee\` | Marketplace fee. Exclusive to OAuth integrations. | | \`integration\_data.application\_id\` | Identifier of the Mercado Pago application that created the order. | | \`integration\_data.platform\_id\` | Platform identifier, assigned by Mercado Pago. | | \`integration\_data.integrator\_id\` | Integrator identifier, assigned by Mercado Pago. | | \`integration\_data.sponsor.id\` | \`USER\_ID\` of the integrating system. | | \`status\` | Current order status. When canceled it is \`canceled\`. | | \`status\_detail\` | Current order status detail. When canceled it is \`canceled\`. | | \`currency\` | Currency identifier used. The possible values are \`ARS\`, \`BRL\`, \`CLP\`, \`UYU\`. | | \`created\_date\` | Order creation date. Format \`yyyy-MM-ddTHH:mm:ss.sssZ\`. | | \`last\_updated\_date\` | Order last update date. Format \`yyyy-MM-ddTHH:mm:ss.sssZ\`. | | \`config.qr.external\_pos\_id\` | External point-of-sale identifier associated with the order. | | \`config.qr.mode\` | Configured QR Code mode. The possible values are \`static\`, \`dynamic\`, \`hybrid\`. For integrations migrated from the Instore Orders API, the value is \`static\`. | | \`transactions.payments\[\].id\` | Payment transaction identifier, generated by Mercado Pago. | | \`transactions.payments\[\].amount\` | Payment amount. | | \`transactions.payments\[\].status\` | Payment status. When canceled it is \`canceled\`. | | \`transactions.payments\[\].status\_detail\` | Payment status detail. When canceled it is \`canceled\_by\_api\`. | | \`transactions.payments\[\].reference\_id\` | Payment identifier associated with the order. | | \`items\[\].title\` | Item name. | | \`items\[\].unit\_price\` | Item unit price. | | \`items\[\].quantity\` | Item quantity. | | \`items\[\].unit\_measure\` | Item unit of measure. | | \`items\[\].external\_code\` | External item code (e.g.: EAN). | ::: :::AccordionComponent{title="Update error handling in cancellation" pill="2"} In cancellation, errors are grouped by type of change. See the tables below for more details. ### Errors that disappear The following errors exist in the Instore Orders API but \*\*have no equivalent\*\* in the Orders API. | HTTP | Instore Orders API | Note | |---|---|---| | \`400\` | \`invalid\_collector\_id\` | Removed in the Orders API. | | \`401\` | \`unauthorized\` (authorization value not present) | Removed. Occurred when sending an alphanumeric \`{user\_id}\`. Identity is obtained directly from the Access Token. | | \`403\` | \`forbidden\` | Removed. Occurred when the \`{user\_id}\` did not belong to the Access Token owner. | ### Renamed errors The following errors were renamed in the Orders API. | HTTP | Instore Orders API | Orders API | Note | |---|---|---|---| | \`400\` → \`404\` | \`pos\_obtainment\_by\_external\_id\_error\` | \`order\_not\_found\` | In the Instore Orders API, it was \`400\`. In the Orders API, it is \`404\`. Cancellation now operates on the \`order\_id\`. | ### Errors that change behavior The following errors exist in both APIs but with different behavior in the Orders API. | HTTP | Instore Orders API | Orders API | Note | |---|---|---|---| | \`400\` → \`401\` | \`invalid\_access\_token\` | \`unauthorized\` | In the Instore Orders API, it was \`400\`. In the Orders API, it is \`401\`. | ### Errors that remain unchanged The following error has the same behavior in both APIs. | HTTP | Error | Note | |---|---|---| | \`500\` | Generic | Internal error. Check the response and retry the request. | ### Errors introduced by the Orders API The following errors have no equivalent in the Instore Orders API. | HTTP | Error | Note | |---|---|---| | \`400\` | \`empty\_required\_header\` | \`X-Idempotency-Key\` missing. | | \`400\` | \`invalid\_path\_param\` | The \`order\_id\` has an invalid format. It must start with \`ORD\` followed by 26 characters. | | \`404\` | \`order\_not\_found\` | The \`order\_id\` does not correspond to any created order. | | \`409\` | \`idempotency\_key\_already\_used\` | The \`X-Idempotency-Key\` was already used with a different request. | | \`409\` | \`order\_already\_canceled\` | The order is already canceled. Orders can only be canceled with \`status: created\`. | | \`409\` | \`instore\_order\_locked\_error\` | The order cannot be canceled while the payment is in progress. | ::: ## Implement refunds with the dedicated endpoint The Orders API introduces the dedicated endpoint :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/refund" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/refund-order/post"} for refunds, which did not exist in the Instore Orders API. Previously, it was necessary to receive the payment webhook, obtain the \`payment\_id\`, and execute the refund through the Payments API separately. Now, the refund is performed directly on the order. :::::AccordionComponent{title="Implement the Orders API refund endpoint" pill="1"} > WARNING > > The Payments API \*\*must not be used\*\* in integrations with the Orders API. All operations, including refunds, must be performed through the Orders API. To refund the full order amount, send the request to the :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/refund" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/refund-order/post"} endpoint without a body. \`\`\`curl curl -X POST \\ 'https://api.mercadopago.com/v1/orders/{{ORDER\_ID}}/refund' \\ -H 'Authorization: Bearer {{ACCESS\_TOKEN}}' \\ -H 'X-Idempotency-Key: {{IDEMPOTENCY\_KEY}}' \`\`\` Refunds are accepted up to 360 days from the payment date. For orders with cash withdrawal (\*cash-out\* or \*extracash\*), the maximum period is 72 hours. > SUCCESS\_MESSAGE > > See all available parameters in the :TagComponent{tag="API" text="API Reference" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/refund-order/post"}. For all possible error codes, see \[Refund errors\](https://www.mercadopago.com.ar/developers/en/docs/qr-code-ca/resources/refund-errors). ::::: :::AccordionComponent{title="Adapt the refund response fields" pill="2"} The refund response varies depending on the type of operation performed. | Field | Type | Description | |---|---|---| | \`id\` | \`string\` | Identifier of the refunded order. | | \`status\` | \`string\` | \`refunded\` for a full refund. | | \`status\_detail\` | \`string\` | \`refunded\` for a full refund. | | \`transactions.refunds\[\].id\` | \`string\` | Refund identifier, generated by Mercado Pago. | | \`transactions.refunds\[\].transaction\_id\` | \`string\` | Identifier of the payment transaction being refunded. | | \`transactions.refunds\[\].amount\` | \`string\` | Refund amount. If the payment was made with a discount, reflects that amount. | | \`transactions.refunds\[\].status\` | \`string\` | \`processing\` when the refund was requested and is being processed. | | \`transactions.refunds\[\].reference\_id\` | \`string\` | Identifier linking the payment to its refund. | ::: ## Validate the migration After applying the changes, verify that the integration works correctly across all flows before going to production. :::CheckboxComponent{label="X-Ttl-Store-Preference header removed" defaultChecked="false"} The \`X-Ttl-Store-Preference\` \*header\* was removed and replaced by the \`expiration\_time\` field in the request \*body\*. ::: :::CheckboxComponent{label="X-Idempotency-Key header configured" defaultChecked="false"} The \`X-Idempotency-Key\` \*header\* must be present in all creation, cancellation, and refund operations. ::: :::CheckboxComponent{label="Order creation validated" defaultChecked="false"} Orders successfully created with the new \*payload\* at the :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"} endpoint and the \`id\` field captured from the \*response\*. ::: :::CheckboxComponent{label="Order query validated" defaultChecked="false"} Orders successfully retrieved via the :TagComponent{tag="API" text="GET /v1/orders/{order\_id}" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/get-order/get"} endpoint. ::: :::CheckboxComponent{label="Status webhook configured" defaultChecked="false"} Order statuses monitored via webhook with the \*\*"Order (Mercado Pago)"\*\* topic and the new values: \`created\`, \`processed\`, \`canceled\`, \`refunded\`, and \`expired\`. ::: :::CheckboxComponent{label="Order cancellation validated" defaultChecked="false"} Orders successfully canceled via the :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/cancel" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/cancel-order/post"} endpoint. ::: :::CheckboxComponent{label="Refunds validated" defaultChecked="false"} Refunds processed via the dedicated :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/refund" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/refund-order/post"} endpoint. ::: See the documentation to learn how to \[test the integration\](https://www.mercadopago.com.ar/developers/en/docs/qr-code/test-integration).