@deenruv/merchant-plugin 1.0.17-dev.18 → 1.0.17-dev.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +14 -0
  2. package/dist/plugin-server/api/platform-integration-admin-resolver.d.ts +20 -1
  3. package/dist/plugin-server/api/platform-integration-admin-resolver.js +44 -19
  4. package/dist/plugin-server/entities/merchant-sync-item.entity.d.ts +14 -0
  5. package/dist/plugin-server/entities/merchant-sync-item.entity.js +56 -0
  6. package/dist/plugin-server/entities/merchant-sync-run.entity.d.ts +19 -0
  7. package/dist/plugin-server/entities/merchant-sync-run.entity.js +72 -0
  8. package/dist/plugin-server/extensions/api-extensions.js +53 -2
  9. package/dist/plugin-server/index.js +10 -1
  10. package/dist/plugin-server/services/google-merchant-api.d.ts +48 -6
  11. package/dist/plugin-server/services/google-merchant-api.js +247 -32
  12. package/dist/plugin-server/services/google-platform-integration.service.d.ts +8 -7
  13. package/dist/plugin-server/services/google-platform-integration.service.js +79 -6
  14. package/dist/plugin-server/services/merchant-sync-history.service.d.ts +25 -0
  15. package/dist/plugin-server/services/merchant-sync-history.service.js +97 -0
  16. package/dist/plugin-server/services/platform-integration.service.d.ts +6 -2
  17. package/dist/plugin-server/services/platform-integration.service.js +53 -29
  18. package/dist/plugin-server/services/subscriber.service.d.ts +32 -4
  19. package/dist/plugin-server/services/subscriber.service.js +141 -23
  20. package/dist/plugin-server/ui/graphql/mutations.ts +7 -0
  21. package/dist/plugin-server/ui/graphql/queries.ts +99 -0
  22. package/dist/plugin-server/ui/pages/GooglePage.tsx +158 -16
  23. package/dist/plugin-server/ui/zeus/const.ts +106 -1
  24. package/dist/plugin-server/ui/zeus/index.ts +482 -10
  25. package/dist/plugin-server/zeus/const.js +104 -1
  26. package/dist/plugin-server/zeus/index.d.ts +492 -6
  27. package/dist/plugin-ui/graphql/mutations.d.ts +9 -4
  28. package/dist/plugin-ui/graphql/mutations.js +6 -0
  29. package/dist/plugin-ui/graphql/queries.d.ts +55 -4
  30. package/dist/plugin-ui/graphql/queries.js +49 -0
  31. package/dist/plugin-ui/pages/GooglePage.js +51 -13
  32. package/dist/plugin-ui/zeus/const.js +104 -1
  33. package/dist/plugin-ui/zeus/index.d.ts +492 -6
  34. package/package.json +8 -7
@@ -15,3 +15,9 @@ export const saveMerchantPlatformSettings = mutation({
15
15
  export const removeOrphanItems = mutation({
16
16
  removeOrphanItems: [{ platform: $("platform", "String!") }, true],
17
17
  });
18
+ export const sendAllProductsToMerchantPlatform = mutation({
19
+ sendAllProductsToMerchantPlatform: [
20
+ { platform: $("platform", "String!") },
21
+ true,
22
+ ],
23
+ });
@@ -1,15 +1,16 @@
1
- export declare const getMerchantPlatformSettings: import("@graphql-typed-document-node/core").TypedDocumentNode<{
1
+ import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
2
+ export declare const getMerchantPlatformSettings: TypedDocumentNode<{
2
3
  getMerchantPlatformSettings?: {
3
- entries?: {
4
+ entries: {
4
5
  value: string;
5
6
  key: string;
6
- }[] | undefined;
7
+ }[];
7
8
  platform: string;
8
9
  } | undefined;
9
10
  }, {} & {
10
11
  platform: string;
11
12
  }>;
12
- export declare const getMerchantPlatformInfo: import("@graphql-typed-document-node/core").TypedDocumentNode<{
13
+ export declare const getMerchantPlatformInfo: TypedDocumentNode<{
13
14
  getMerchantPlatformInfo?: {
14
15
  isValidConnection: boolean;
15
16
  productsCount: number;
@@ -17,3 +18,53 @@ export declare const getMerchantPlatformInfo: import("@graphql-typed-document-no
17
18
  }, {} & {
18
19
  platform: string;
19
20
  }>;
21
+ export type GoogleMerchantDiagnostic = {
22
+ checkedAt?: string;
23
+ connectionStatus?: string;
24
+ dataSourceVerified?: boolean;
25
+ disapprovedProductsCount?: number;
26
+ isValidConnection: boolean;
27
+ issues?: Array<{
28
+ code: string;
29
+ description: string;
30
+ offerId: string;
31
+ severity: string;
32
+ }>;
33
+ issuesCount?: number;
34
+ lastError?: {
35
+ code: string;
36
+ message: string;
37
+ retryable: boolean;
38
+ };
39
+ latencyMs?: number;
40
+ productsCount: number;
41
+ };
42
+ export type MerchantSyncRunSummary = {
43
+ createdAt: string;
44
+ errorSummary?: string;
45
+ failed: number;
46
+ finishedAt?: string;
47
+ id: string;
48
+ items: Array<{
49
+ attempts: number;
50
+ errorCode?: string;
51
+ errorMessage?: string;
52
+ offerId: string;
53
+ operation: string;
54
+ status: string;
55
+ }>;
56
+ status: string;
57
+ succeeded: number;
58
+ total: number;
59
+ trigger: string;
60
+ };
61
+ export declare const getGoogleMerchantDiagnostic: TypedDocumentNode<{
62
+ getMerchantPlatformInfo?: GoogleMerchantDiagnostic[] | undefined;
63
+ }, {
64
+ [key: string]: any;
65
+ }>;
66
+ export declare const getGoogleMerchantSyncHistory: TypedDocumentNode<{
67
+ getMerchantSyncHistory: MerchantSyncRunSummary[];
68
+ }, {
69
+ take?: number | undefined;
70
+ }>;
@@ -2,6 +2,7 @@ import { typedGql } from "../zeus/typedDocumentNode";
2
2
  import { scalars } from "./scalars";
3
3
  import { merchantPlatformSettingsSelector } from "./mutations";
4
4
  import { $ } from "../zeus/index";
5
+ import { gql } from "graphql-tag";
5
6
  const query = typedGql("query", { scalars });
6
7
  export const getMerchantPlatformSettings = query({
7
8
  getMerchantPlatformSettings: [
@@ -15,3 +16,51 @@ export const getMerchantPlatformInfo = query({
15
16
  { isValidConnection: true, productsCount: true },
16
17
  ],
17
18
  });
19
+ export const getGoogleMerchantDiagnostic = gql `
20
+ query GetGoogleMerchantDiagnostic {
21
+ getMerchantPlatformInfo(platform: "google") {
22
+ isValidConnection
23
+ productsCount
24
+ connectionStatus
25
+ dataSourceVerified
26
+ checkedAt
27
+ latencyMs
28
+ disapprovedProductsCount
29
+ issuesCount
30
+ lastError {
31
+ code
32
+ message
33
+ retryable
34
+ }
35
+ issues {
36
+ offerId
37
+ code
38
+ description
39
+ severity
40
+ }
41
+ }
42
+ }
43
+ `;
44
+ export const getGoogleMerchantSyncHistory = gql `
45
+ query GetGoogleMerchantSyncHistory($take: Int) {
46
+ getMerchantSyncHistory(platform: "google", take: $take) {
47
+ id
48
+ createdAt
49
+ trigger
50
+ status
51
+ total
52
+ succeeded
53
+ failed
54
+ errorSummary
55
+ finishedAt
56
+ items {
57
+ offerId
58
+ operation
59
+ status
60
+ errorCode
61
+ errorMessage
62
+ attempts
63
+ }
64
+ }
65
+ }
66
+ `;
@@ -1,14 +1,18 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
3
  import { Label, Input, PageBlock, useLazyQuery, useMutation, Checkbox, Button, } from "@deenruv/react-ui-devkit";
4
- import { getMerchantPlatformInfo, getMerchantPlatformSettings, } from "../graphql/queries";
5
- import { removeOrphanItems, saveMerchantPlatformSettings, } from "../graphql/mutations";
4
+ import { getGoogleMerchantDiagnostic, getGoogleMerchantSyncHistory, getMerchantPlatformSettings, } from "../graphql/queries";
5
+ import { removeOrphanItems, saveMerchantPlatformSettings, sendAllProductsToMerchantPlatform, } from "../graphql/mutations";
6
6
  import { toast } from "sonner";
7
7
  export const GooglePage = () => {
8
8
  const [fetchMerchantPlatformSettings] = useLazyQuery(getMerchantPlatformSettings);
9
- const [fetchMerchantPlatformInfo] = useLazyQuery(getMerchantPlatformInfo);
9
+ const [fetchMerchantPlatformInfo] = useLazyQuery(getGoogleMerchantDiagnostic);
10
+ const [fetchSyncHistory] = useLazyQuery(getGoogleMerchantSyncHistory);
10
11
  const [mutate] = useMutation(saveMerchantPlatformSettings);
11
12
  const [removeOldItems] = useMutation(removeOrphanItems);
13
+ const [sendAllProducts] = useMutation(sendAllProductsToMerchantPlatform);
14
+ const [diagnostic, setDiagnostic] = useState(null);
15
+ const [syncHistory, setSyncHistory] = useState([]);
12
16
  const [serviceInfo, setServiceInfo] = useState({
13
17
  productsCount: 0,
14
18
  connectionStatus: false,
@@ -42,6 +46,8 @@ export const GooglePage = () => {
42
46
  brand: "",
43
47
  merchantId: "",
44
48
  dataSource: "",
49
+ contentLanguage: "pl",
50
+ feedLabel: "PL",
45
51
  credentials: "",
46
52
  autoUpdate: true,
47
53
  firstSync: true,
@@ -49,9 +55,10 @@ export const GooglePage = () => {
49
55
  const refetch = async () => {
50
56
  try {
51
57
  setIsLoading(true);
52
- const [settingsData, infoData] = await Promise.all([
58
+ const [settingsData, infoData, historyData] = await Promise.all([
53
59
  fetchMerchantPlatformSettings({ platform: "google" }),
54
- fetchMerchantPlatformInfo({ platform: "google" }),
60
+ fetchMerchantPlatformInfo(),
61
+ fetchSyncHistory({ take: 10 }),
55
62
  ]);
56
63
  const settings = settingsData?.getMerchantPlatformSettings?.entries?.reduce((acc, { key, value }) => {
57
64
  if (key === "brand") {
@@ -63,6 +70,12 @@ export const GooglePage = () => {
63
70
  if (key === "dataSource") {
64
71
  acc.dataSource = value;
65
72
  }
73
+ if (key === "contentLanguage") {
74
+ acc.contentLanguage = value;
75
+ }
76
+ if (key === "feedLabel") {
77
+ acc.feedLabel = value;
78
+ }
66
79
  if (key === "credentials") {
67
80
  acc.credentials = value;
68
81
  }
@@ -75,9 +88,12 @@ export const GooglePage = () => {
75
88
  return acc;
76
89
  }, {});
77
90
  setSettingsForm((prev) => ({ ...prev, ...settings }));
91
+ const currentDiagnostic = infoData?.getMerchantPlatformInfo?.[0];
92
+ setDiagnostic(currentDiagnostic ?? null);
93
+ setSyncHistory(historyData?.getMerchantSyncHistory ?? []);
78
94
  setServiceInfo({
79
- productsCount: infoData?.getMerchantPlatformInfo?.[0]?.productsCount || 0,
80
- connectionStatus: infoData?.getMerchantPlatformInfo?.[0]?.isValidConnection || false,
95
+ productsCount: currentDiagnostic?.productsCount || 0,
96
+ connectionStatus: currentDiagnostic?.isValidConnection || false,
81
97
  });
82
98
  setIsLoading(false);
83
99
  }
@@ -111,7 +127,7 @@ export const GooglePage = () => {
111
127
  }
112
128
  catch (error) {
113
129
  console.error(error);
114
- toast.error("Failed to save settings");
130
+ toast.error(`Failed to save settings: ${error instanceof Error ? error.message : "Unknown error"}`);
115
131
  }
116
132
  };
117
133
  return (_jsx(PageBlock, { children: _jsxs("div", { style: { position: "relative" }, className: "flex flex-col p-4 relative max-w-[800px] mx-auto", children: [isLoading && (_jsx("div", { style: {
@@ -138,7 +154,13 @@ export const GooglePage = () => {
138
154
  }) })] })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "Data source" }), _jsx(Input, { className: "w-full", placeholder: "accounts/{merchantId}/dataSources/{id}", required: true, value: settingsForm.dataSource, onChange: (e) => setSettingsForm({
139
155
  ...settingsForm,
140
156
  dataSource: e.target.value,
141
- }) })] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col", children: [_jsx(Label, { children: "Google Account Credentials" }), _jsx(Input, { style: {
157
+ }) })] }), _jsxs("div", { className: "flex justify-between gap-4", children: [_jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "Content language" }), _jsx(Input, { required: true, value: settingsForm.contentLanguage, onChange: (e) => setSettingsForm({
158
+ ...settingsForm,
159
+ contentLanguage: e.target.value,
160
+ }) })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "Feed label" }), _jsx(Input, { required: true, value: settingsForm.feedLabel, onChange: (e) => setSettingsForm({
161
+ ...settingsForm,
162
+ feedLabel: e.target.value,
163
+ }) })] })] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col", children: [_jsx(Label, { children: "Google Account Credentials" }), _jsx(Input, { style: {
142
164
  border: "none",
143
165
  backgroundColor: "transparent",
144
166
  background: "none",
@@ -148,15 +170,31 @@ export const GooglePage = () => {
148
170
  }) }), _jsx(Label, { htmlFor: "google-account-credentials", children: "Auto update on Product's change" })] })] }), _jsx("div", { className: "flex justify-end", children: _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex gap-2", children: [_jsx(Label, { htmlFor: "auto-update-on-products-change", children: "Update ALL products with saving" }), _jsx(Checkbox, { id: "auto-update-on-products-change", checked: settingsForm.firstSync, onCheckedChange: (checked) => setSettingsForm({
149
171
  ...settingsForm,
150
172
  firstSync: typeof checked === "boolean" ? checked : false,
151
- }) })] }), _jsx(Button, { children: "Save" })] }) })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("span", { children: "Connection status" }), serviceInfo.connectionStatus ? _jsx("div", { children: "\uD83D\uDC9A" }) : _jsx("div", { children: "\uD83D\uDC94" })] }), serviceInfo.connectionStatus ? (_jsx("div", { className: "mt-8", children: _jsx(Button, { onClick: async () => {
173
+ }) })] }), _jsx(Button, { children: "Save" })] }) })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("span", { children: "Connection status" }), _jsx("strong", { children: serviceInfo.connectionStatus ? "Connected" : "Disconnected" })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("span", { children: "Products count" }), _jsx("span", { children: serviceInfo.productsCount })] }), _jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [_jsxs("span", { children: ["Diagnostic: ", diagnostic?.connectionStatus ?? "UNKNOWN"] }), _jsxs("span", { children: ["Data source verified:", " ", diagnostic?.dataSourceVerified ? "yes" : "no"] }), _jsxs("span", { children: ["Latency: ", diagnostic?.latencyMs ?? 0, " ms"] }), _jsxs("span", { children: ["Disapproved products: ", diagnostic?.disapprovedProductsCount ?? 0] }), _jsxs("span", { children: ["Product issues: ", diagnostic?.issuesCount ?? 0] }), diagnostic?.lastError ? (_jsxs("div", { className: "rounded border border-red-400 p-2 text-red-700", children: [diagnostic.lastError.code, ": ", diagnostic.lastError.message] })) : null, (diagnostic?.issues ?? []).slice(0, 20).map((issue) => (_jsxs("div", { className: "rounded border border-amber-400 p-2", children: [issue.offerId, " \u00B7 ", issue.severity, " \u00B7 ", issue.code, ":", " ", issue.description] }, `${issue.offerId}-${issue.code}`)))] }), _jsxs("div", { className: "mt-4 flex gap-2", children: [_jsx(Button, { type: "button", onClick: async () => {
174
+ await refetch();
175
+ toast.success("Connection diagnostic completed");
176
+ }, children: "Test connection" }), _jsx(Button, { type: "button", onClick: async () => {
177
+ try {
178
+ await sendAllProducts({ platform: "google" });
179
+ toast.success("Full synchronization queued");
180
+ await refetch();
181
+ }
182
+ catch (error) {
183
+ console.error(error);
184
+ toast.error("Failed to queue full synchronization");
185
+ }
186
+ }, children: "Synchronize all" })] }), serviceInfo.connectionStatus ? (_jsx("div", { className: "mt-8", children: _jsx(Button, { onClick: async () => {
152
187
  try {
153
188
  await removeOldItems({ platform: "google" });
154
- toast.success("Old items removed successfully");
189
+ toast.success("Orphan cleanup queued");
155
190
  refetch();
156
191
  }
157
192
  catch (error) {
158
193
  console.error(error);
159
- toast.error("Failed to remove old items");
194
+ toast.error("Failed to queue orphan cleanup");
160
195
  }
161
- }, children: "Remove old items" }) })) : null] }) }));
196
+ }, children: "Remove old items" }) })) : null, _jsxs("div", { className: "mt-8 flex flex-col gap-2", children: [_jsx("h3", { className: "font-semibold", children: "Recent synchronizations" }), syncHistory.length === 0 ? (_jsx("span", { children: "No synchronization history" })) : (syncHistory.map((run) => (_jsxs("div", { className: "rounded border p-2 text-sm", children: [_jsxs("div", { children: [run.status, " \u00B7 ", run.succeeded, "/", run.total, " successful \u00B7", " ", run.failed, " failed"] }), _jsx("div", { children: new Date(run.createdAt).toLocaleString() }), run.errorSummary ? (_jsx("div", { className: "text-red-700", children: run.errorSummary })) : null, run.items
197
+ .filter((item) => item.status === "FAILED")
198
+ .slice(0, 20)
199
+ .map((item) => (_jsxs("div", { className: "mt-1 text-red-700", children: [item.offerId, " \u00B7 ", item.errorCode ?? "ERROR", " \u00B7 attempts:", " ", item.attempts, " \u00B7 ", item.errorMessage] }, `${run.id}-${item.offerId}-${item.operation}`)))] }, run.id))))] })] }) }));
162
200
  };
@@ -116,6 +116,13 @@ export const AllTypesProps = {
116
116
  zone: {},
117
117
  getMerchantPlatformSettings: {},
118
118
  getMerchantPlatformInfo: {},
119
+ getMerchantSyncHistory: {},
120
+ chartMetric: {
121
+ input: "ChartMetricInput"
122
+ },
123
+ orderSummaryMetric: {
124
+ input: "OrderSummaryMetricInput"
125
+ },
119
126
  getInpostOrganizations: {
120
127
  input: "GetInpostOrganizationsInput"
121
128
  },
@@ -1178,6 +1185,21 @@ export const AllTypesProps = {
1178
1185
  SaveMerchantPlatformSettingInput: {
1179
1186
  entries: "MerchantPlatformSettingInput"
1180
1187
  },
1188
+ MetricRangeType: "enum",
1189
+ MetricIntervalType: "enum",
1190
+ ChartMetricType: "enum",
1191
+ BetterMetricRangeInput: {
1192
+ start: "DateTime",
1193
+ end: "DateTime"
1194
+ },
1195
+ OrderSummaryMetricInput: {
1196
+ range: "BetterMetricRangeInput"
1197
+ },
1198
+ ChartMetricInput: {
1199
+ range: "BetterMetricRangeInput",
1200
+ interval: "MetricIntervalType",
1201
+ types: "ChartMetricType"
1202
+ },
1181
1203
  SetInpostShippingMethodConfigInput: {},
1182
1204
  GetInpostOrganizationsInput: {},
1183
1205
  MetricInterval: "enum",
@@ -1698,6 +1720,10 @@ export const ReturnTypes = {
1698
1720
  zone: "Zone",
1699
1721
  getMerchantPlatformSettings: "MerchantPlatformSettingsEntity",
1700
1722
  getMerchantPlatformInfo: "MerchantPlatformInfo",
1723
+ getMerchantSyncHistory: "MerchantSyncRun",
1724
+ additionalOrderStates: "AdditionalOrderState",
1725
+ chartMetric: "ChartMetrics",
1726
+ orderSummaryMetric: "OrderSummaryMetrics",
1701
1727
  getInpostConfig: "InpostConfig",
1702
1728
  getInpostOrganizations: "InpostOrganizationResponse",
1703
1729
  metricSummary: "MetricSummary"
@@ -3606,7 +3632,84 @@ export const ReturnTypes = {
3606
3632
  },
3607
3633
  MerchantPlatformInfo: {
3608
3634
  isValidConnection: "Boolean",
3609
- productsCount: "Int"
3635
+ productsCount: "Int",
3636
+ connectionStatus: "String",
3637
+ dataSourceVerified: "Boolean",
3638
+ checkedAt: "DateTime",
3639
+ latencyMs: "Int",
3640
+ disapprovedProductsCount: "Int",
3641
+ issuesCount: "Int",
3642
+ lastError: "MerchantPlatformError",
3643
+ issues: "MerchantProductIssue"
3644
+ },
3645
+ MerchantPlatformError: {
3646
+ code: "String",
3647
+ message: "String",
3648
+ retryable: "Boolean"
3649
+ },
3650
+ MerchantProductIssue: {
3651
+ offerId: "String",
3652
+ code: "String",
3653
+ description: "String",
3654
+ severity: "String"
3655
+ },
3656
+ MerchantSyncItem: {
3657
+ id: "ID",
3658
+ offerId: "String",
3659
+ operation: "String",
3660
+ status: "String",
3661
+ errorCode: "String",
3662
+ errorMessage: "String",
3663
+ attempts: "Int"
3664
+ },
3665
+ MerchantSyncRun: {
3666
+ id: "ID",
3667
+ createdAt: "DateTime",
3668
+ platform: "String",
3669
+ trigger: "String",
3670
+ status: "String",
3671
+ jobId: "String",
3672
+ total: "Int",
3673
+ succeeded: "Int",
3674
+ failed: "Int",
3675
+ errorSummary: "String",
3676
+ startedAt: "DateTime",
3677
+ finishedAt: "DateTime",
3678
+ items: "MerchantSyncItem"
3679
+ },
3680
+ AdditionalOrderState: {
3681
+ state: "String",
3682
+ selectedByDefault: "Boolean"
3683
+ },
3684
+ ChartDataType: {
3685
+ type: "ChartMetricType",
3686
+ title: "String",
3687
+ entries: "ChartEntry"
3688
+ },
3689
+ ChartMetrics: {
3690
+ data: "ChartDataType"
3691
+ },
3692
+ OrderSummaryMetrics: {
3693
+ data: "OrderSummaryDataMetric"
3694
+ },
3695
+ OrderSummaryDataMetric: {
3696
+ currencyCode: "CurrencyCode",
3697
+ total: "Float",
3698
+ totalWithTax: "Float",
3699
+ orderCount: "Float",
3700
+ averageOrderValue: "Float",
3701
+ averageOrderValueWithTax: "Float",
3702
+ productCount: "Float"
3703
+ },
3704
+ ChartEntryAdditionalData: {
3705
+ id: "String",
3706
+ name: "String",
3707
+ quantity: "Float"
3708
+ },
3709
+ ChartEntry: {
3710
+ intervalTick: "Int",
3711
+ value: "Float",
3712
+ additionalData: "ChartEntryAdditionalData"
3610
3713
  },
3611
3714
  InpostConfig: {
3612
3715
  shippingMethodId: "ID",