|
| 1 | +# checkout-data-sync |
| 2 | + |
| 3 | +[](https://github.com/commercetools/checkout-data-sync/actions) |
| 4 | +[](https://hub.docker.com/r/commercetools/checkout-data-sync) |
| 5 | + |
| 6 | +<!-- START doctoc generated TOC please keep comment here to allow auto update --> |
| 7 | +<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> |
| 8 | + |
| 9 | +- [What is this?](#what-is-this) |
| 10 | +- [Prerequisites](#prerequisites) |
| 11 | +- [Usage](#usage) |
| 12 | + - [Running the Docker Image](#running-the-docker-image) |
| 13 | + - [Build](#build) |
| 14 | + - [Run](#run) |
| 15 | +- [Examples](#examples) |
| 16 | +- [Sync Behaviour](#sync-behaviour) |
| 17 | + - [Applications](#applications) |
| 18 | + - [Payment Integrations](#payment-integrations) |
| 19 | +- [Scopes](#scopes) |
| 20 | + |
| 21 | +<!-- END doctoc generated TOC please keep comment here to allow auto update --> |
| 22 | + |
| 23 | +### What is this? |
| 24 | + |
| 25 | +A Dockerized CLI application which migrates [commercetools Checkout](https://docs.commercetools.com/checkout) resources between projects — for example when moving between cloud providers or regions. |
| 26 | + |
| 27 | +The following resource types are supported, synced in this order: |
| 28 | + |
| 29 | +- **Applications** |
| 30 | +- **Payment Integrations** |
| 31 | + |
| 32 | +Applications are synced first because Payment Integrations reference them by ID. The tool builds a `sourceAppID → targetAppID` map during the application phase and uses it to rewrite references before touching payment integrations. |
| 33 | + |
| 34 | +### Prerequisites |
| 35 | + |
| 36 | +- Docker (to run the image) or Go 1.21+ (to build from source) |
| 37 | +- A commercetools API client with Checkout permissions on both source and target projects |
| 38 | +- A `config.yml` file — copy the example and fill in your credentials: |
| 39 | + |
| 40 | + ```bash |
| 41 | + cp config.example.yml config.yml |
| 42 | + ``` |
| 43 | + |
| 44 | + ```yaml |
| 45 | + source: |
| 46 | + project_key: "source-project-key" |
| 47 | + client_id: "sourceClientId" |
| 48 | + client_secret: "sourceClientSecret" |
| 49 | + # OAuth token endpoint (Composable Commerce), not the Checkout API host. |
| 50 | + auth_url: "https://auth.europe-west1.gcp.commercetools.com/oauth/token" |
| 51 | + checkout_api_url: "https://checkout.europe-west1.gcp.commercetools.com" |
| 52 | + # Optional: space-separated OAuth scopes. Omit to use the API client's default scopes. |
| 53 | + scopes: "manage_project" |
| 54 | + |
| 55 | + target: |
| 56 | + project_key: "target-project-key" |
| 57 | + client_id: "targetClientId" |
| 58 | + client_secret: "targetClientSecret" |
| 59 | + auth_url: "https://auth.eu-central-1.aws.commercetools.com/oauth/token" |
| 60 | + checkout_api_url: "https://checkout.eu-central-1.aws.commercetools.com" |
| 61 | + scopes: "" |
| 62 | + |
| 63 | + # Map source connector deployment UUIDs to target deployment UUIDs. |
| 64 | + # Required for every Payment Integration that has a connectorDeployment. |
| 65 | + # Connector deployment IDs are environment-specific and will not resolve |
| 66 | + # across cloud providers without an explicit mapping. |
| 67 | + deployment_mapping: |
| 68 | + "source-deployment-uuid": "target-deployment-uuid" |
| 69 | + ``` |
| 70 | +
|
| 71 | + > **Note:** `auth_url` and `checkout_api_url` must not have a trailing slash. |
| 72 | + |
| 73 | +- The following fields are **required** to be set on resources that will be synced: |
| 74 | + |
| 75 | + | Resource | Required Fields | |
| 76 | + |---|---| |
| 77 | + | Application | `key` | |
| 78 | + | Payment Integration | `key` (derived from source ID + name if absent — see [Sync Behaviour](#payment-integrations)) | |
| 79 | + |
| 80 | +### Usage |
| 81 | + |
| 82 | +``` |
| 83 | +usage: checkout-data-sync |
| 84 | + -c, --config <path> Path to config file (default: config.yml) |
| 85 | + -f, --full Execute the migration. Omit to perform a dry-run |
| 86 | + instead (shows what would be created or updated, |
| 87 | + without making any changes). |
| 88 | +``` |
| 89 | +
|
| 90 | +By default the tool runs in **dry-run** mode: it compares source and target resources and prints a plan without writing anything. Pass `-f` to execute the migration. |
| 91 | +
|
| 92 | +#### Running the Docker Image |
| 93 | +
|
| 94 | +##### Build |
| 95 | +
|
| 96 | +```bash |
| 97 | +docker build -t checkout-data-sync . |
| 98 | +``` |
| 99 | + |
| 100 | +##### Run |
| 101 | + |
| 102 | +Mount your `config.yml` at `/app/config.yml` via a volume — credentials must never be baked into the image. |
| 103 | + |
| 104 | +```bash |
| 105 | +docker run --rm \ |
| 106 | + -v $(pwd)/config.yml:/app/config.yml \ |
| 107 | + checkout-data-sync |
| 108 | +``` |
| 109 | + |
| 110 | +### Examples |
| 111 | + |
| 112 | +- To perform a dry-run (default): |
| 113 | + ```bash |
| 114 | + docker run --rm -v $(pwd)/config.yml:/app/config.yml checkout-data-sync |
| 115 | + ``` |
| 116 | + |
| 117 | + Example output: |
| 118 | + ``` |
| 119 | + === DRY RUN — pass -f to execute === |
| 120 | +
|
| 121 | + --- Applications --- |
| 122 | + Found 2 application(s) in source project "source-project-key" |
| 123 | +
|
| 124 | + [CREATE] application "my-application" |
| 125 | + [SKIP] application "existing-app" — already up to date |
| 126 | +
|
| 127 | + Applications: 1 to create, 0 to update, 1 skipped/errors |
| 128 | +
|
| 129 | + --- Payment Integrations --- |
| 130 | + Found 3 payment integration(s) in source project "source-project-key" |
| 131 | +
|
| 132 | + [CREATE] payment integration "credit-card-via-adyen" |
| 133 | + [UPDATE] payment integration "paypal" (target id: abc123, version: 2) |
| 134 | + • action: setStatus |
| 135 | + [SKIP] payment integration "apple-pay" — already up to date |
| 136 | +
|
| 137 | + Payment integrations: 1 to create, 1 to update, 1 skipped/errors |
| 138 | + ``` |
| 139 | + |
| 140 | +- To execute the migration: |
| 141 | + ```bash |
| 142 | + docker run --rm -v $(pwd)/config.yml:/app/config.yml checkout-data-sync -f |
| 143 | + ``` |
| 144 | + |
| 145 | +- To use a config file at a custom path: |
| 146 | + ```bash |
| 147 | + docker run --rm \ |
| 148 | + -v $(pwd)/staging.yml:/app/staging.yml \ |
| 149 | + checkout-data-sync -c /app/staging.yml -f |
| 150 | + ``` |
| 151 | + |
| 152 | +- To build and run without Docker: |
| 153 | + ```bash |
| 154 | + go build -o checkout-data-sync . |
| 155 | + ./checkout-data-sync -c config.yml # dry-run |
| 156 | + ./checkout-data-sync -c config.yml -f # execute |
| 157 | + ``` |
| 158 | + |
| 159 | +### Sync Behaviour |
| 160 | + |
| 161 | +#### Applications |
| 162 | + |
| 163 | +Applications are matched between source and target by their `key`. |
| 164 | + |
| 165 | +| Condition | Action | |
| 166 | +|---|---| |
| 167 | +| Key not found in target | `CREATE` | |
| 168 | +| Key found, no fields differ | `SKIP` | |
| 169 | +| Key found, fields differ | `UPDATE` | |
| 170 | + |
| 171 | +The following update actions are used: `setName`, `setStatus`, `setDescription`, `setApplicationLogo`, `setCountries`, `setAllowedOrigins`, `setPaymentsConfiguration`, `setDiscountsConfiguration`. |
| 172 | + |
| 173 | +Agreements within an application are synced by `name`: |
| 174 | + |
| 175 | +| Condition | Action | |
| 176 | +|---|---| |
| 177 | +| Agreement name not in target | `addAgreement` | |
| 178 | +| Agreement name not in source | `removeAgreement` | |
| 179 | +| Agreement exists but differs | `setAgreementName` / `setAgreementType` / `setAgreementStatus` / `setAgreementText` | |
| 180 | + |
| 181 | +#### Payment Integrations |
| 182 | + |
| 183 | +Payment integrations are matched against target resources using a two-step lookup: |
| 184 | + |
| 185 | +1. **Exact key match** — looks up the source key in the target index. |
| 186 | +2. **Name fallback** — if no key match, looks up by `name`. When a name match is found with a different key, the target key is considered stale and a `setKey` action is prepended to the update. This handles key renames in the source project. |
| 187 | + |
| 188 | +> **Note:** If multiple target payment integrations share the same name, the name index entry is removed to prevent ambiguous matching. A missing key match in that case results in a new resource being created. |
| 189 | +
|
| 190 | +| Condition | Action | |
| 191 | +|---|---| |
| 192 | +| No match by key or name | `CREATE` | |
| 193 | +| Key match, no fields differ | `SKIP` | |
| 194 | +| Key match, fields differ | `UPDATE` | |
| 195 | +| Name match, key differs | `UPDATE` with `setKey` prepended | |
| 196 | + |
| 197 | +The following update actions are used: `setKey`, `setName`, `setStatus`, `setComponentType`, `setPredicate`, `setDisplayInfo`, `setSortingInfo`, `setAutomatedReversalConfiguration`, `setConnectorDeployment`. |
| 198 | + |
| 199 | +**Key derivation for keyless integrations** |
| 200 | + |
| 201 | +When a payment integration has no `key`, one is derived from the combination of its source **ID** and **name**: `{sanitised-id}-{sanitised-name}`. Using the source ID as a prefix guarantees that two integrations sharing the same display name receive distinct keys. |
| 202 | + |
| 203 | +**Connector deployment mapping** |
| 204 | + |
| 205 | +Connector deployment IDs are environment-specific. Any payment integration that has a `connectorDeployment` **must** have its source deployment UUID listed in `deployment_mapping`. Without a mapping entry the integration is skipped with a clear error: |
| 206 | + |
| 207 | +``` |
| 208 | +payment integration "credit-card-via-adyen": connectorDeployment "src-uuid" has no entry |
| 209 | +in deployment_mapping — add it under deployment_mapping in config.yml and retry |
| 210 | +``` |
| 211 | + |
| 212 | +**Error handling** |
| 213 | + |
| 214 | +Errors on individual resources are non-fatal. The tool logs each failure, continues processing the remaining resources, and exits with a non-zero status when any error occurred. This means a single bad resource never blocks the rest of the migration. |
| 215 | + |
| 216 | +## Scopes |
| 217 | + |
| 218 | +For least-privilege access, use the following scopes instead of `manage_project`: |
| 219 | + |
| 220 | +| Operation | Source scope | Target scope | |
| 221 | +|---|---|---| |
| 222 | +| Read applications | `view_checkout_applications:{projectKey}` | — | |
| 223 | +| Write applications | — | `manage_checkout_applications:{projectKey}` | |
| 224 | +| Read payment integrations | `view_checkout_payment_integrations:{projectKey}` | — | |
| 225 | +| Write payment integrations | — | `manage_checkout_payment_integrations:{projectKey}` | |
| 226 | + |
| 227 | +____ |
| 228 | + |
| 229 | +#### Maintained by: [ogwurujohnson](https://github.com/ogwurujohnson) |
0 commit comments