From 944d379ab119ac62d90cf479d1ff86c76debfb7f Mon Sep 17 00:00:00 2001 From: Christian Michelsen Date: Wed, 24 Jan 2024 09:23:08 +0100 Subject: [PATCH] Messy messy --- sample_code/order-management_sample.ts | 103 +++++++++++ src/apis/ordermanagement.ts | 75 ++++++++ src/apis/types/common_types.ts | 4 + src/apis/types/ordermanagement_types.ts | 218 ++++++++++++++++++++++++ src/mod.ts | 3 + 5 files changed, 403 insertions(+) create mode 100644 sample_code/order-management_sample.ts create mode 100644 src/apis/ordermanagement.ts create mode 100644 src/apis/types/common_types.ts create mode 100644 src/apis/types/ordermanagement_types.ts diff --git a/sample_code/order-management_sample.ts b/sample_code/order-management_sample.ts new file mode 100644 index 0000000..24adcbf --- /dev/null +++ b/sample_code/order-management_sample.ts @@ -0,0 +1,103 @@ +import { open } from "https://deno.land/x/open@v0.0.6/index.ts"; +import "https://deno.land/std@0.208.0/dotenv/load.ts"; +//import { Client } from "https://deno.land/x/vipps_mobilepay_sdk@0.8.0/mod.ts"; +import { Client } from "../src/mod.ts"; // TODO Revert to deno.land/x on commit + +// Read environment variables +const clientId = Deno.env.get("CLIENT_ID") || ""; +const clientSecret = Deno.env.get("CLIENT_SECRET") || ""; + +const merchantSerialNumber = Deno.env.get("MERCHANT_SERIAL_NUMBER") || ""; +const subscriptionKey = Deno.env.get("SUBSCRIPTION_KEY") || ""; +console.log(clientId) +// Create a client +const client = Client({ + merchantSerialNumber, + subscriptionKey, + useTestMode: false, + retryRequests: false, +}); + +// Grab a token +const accessToken = await client.auth.getToken({ + clientId, + clientSecret, + subscriptionKey, +}); + +if (!accessToken.ok) { + console.log("Error retrieving token", accessToken.error); + Deno.exit(1); +} +const token = accessToken.data.access_token; + +// Creating an ePayment order to put a receipt on +const orderReference = crypto.randomUUID(); +const payment = await client.payment.create(token, { + reference: orderReference, + amount: { + currency: "NOK", + value: 100, + }, + paymentMethod: { + type: "WALLET", + }, + userFlow: "WEB_REDIRECT", + returnUrl: "https://example.com/", + paymentDescription: "This is my BOOMstick!", +}); +if (!payment.ok) { + console.log("Error creating payment", payment.error); + Deno.exit(1); +} +console.log("We have a payment, let's open the browser..."); +await open(payment.data.redirectUrl); + +const shouldProceed = confirm("Complete the order, then press 'y' and hit enter."); +console.log("Aaaaaand we continue to the fun part!"); +const receipt = await client.orderManagement.addReceipt( + token, + { + orderLines: [ + { + name: "BOOMstick #1", + id: "sku-1", + totalAmount: 100, + totalAmountExcludingTax: 80, + totalTaxAmount: 20, + taxPercentage: 25, + }, + ], + bottomLine: { + currency: "NOK", + }, + }, + "ecom", + orderReference, +); +if (receipt.ok) { + console.log("Receipt: ", receipt); +} else { + console.log("Error adding receipt", receipt.error); +} + +const fetchedReceipt = await client.orderManagement.getOrderWithCategoryAndReceipt(token, "ecom", orderReference); +console.log(fetchedReceipt); +// const addCategoryToOrderResult = await client.orderManagement +// .addCategoryToOrder(token, "ecom", orderReference, { +// category: "RECEIPT", +// imageId: null, +// orderDetailsUrl: `https://example.com/portal/receipt/${orderReference}`, +// }); + +// if (addCategoryToOrderResult.ok) { +// console.log("Successfully added category to order"); +// console.log("Data:", addCategoryToOrderResult.data); +// } else { +// console.log( +// "Error adding category to order:", +// addCategoryToOrderResult.error, +// ); +// } + + diff --git a/src/apis/ordermanagement.ts b/src/apis/ordermanagement.ts new file mode 100644 index 0000000..fce4554 --- /dev/null +++ b/src/apis/ordermanagement.ts @@ -0,0 +1,75 @@ +import { + OrderManagementAddCategoryOKResponse, + OrderManagementAddImageOKResponse, + OrderManagementAddImageRequest, + OrderManagementAddReceiptOKResponse, + OrderManagementAddReceiptRequest, + OrderManagementErrorResponse, + OrderManagementGetOrderOKResponse, + OrderManagementPaymentType, + TypedOrderManagementAddCategoryToOrderRequest, +} from "./types/ordermanagement_types.ts"; +import { RequestData } from "../types.ts"; + +export const orderManagementRequestFactory = { + addCategoryToOrder( + token: string, + paymentType: OrderManagementPaymentType, + orderId: string, + body: TypedOrderManagementAddCategoryToOrderRequest, + ): RequestData< + OrderManagementAddCategoryOKResponse, + OrderManagementErrorResponse + > { + return { + url: `/order-management/v2/${paymentType}/categories/${orderId}`, + method: "PUT", + body: body, + token, + }; + }, + addImage( + token: string, + body: OrderManagementAddImageRequest, + ): RequestData< + OrderManagementAddImageOKResponse, + OrderManagementErrorResponse + > { + return { + url: "/order-management/v1/images", + method: "POST", + body: body, + token, + }; + }, + getOrderWithCategoryAndReceipt( + token: string, + paymentType: OrderManagementPaymentType, + orderId: string, + ): RequestData< + OrderManagementGetOrderOKResponse, + OrderManagementErrorResponse + > { + return { + url: `/order-management/v2/${paymentType}/${orderId}`, + method: "GET", + token, + }; + }, + addReceipt( + token: string, + body: OrderManagementAddReceiptRequest, + paymentType: OrderManagementPaymentType, + orderId: string, + ): RequestData< + OrderManagementAddReceiptOKResponse, + OrderManagementErrorResponse + > { + return { + url: `/order-management/v2/${paymentType}/receipts/${orderId}`, + method: "POST", + body: body, + token, + }; + }, +} as const; diff --git a/src/apis/types/common_types.ts b/src/apis/types/common_types.ts new file mode 100644 index 0000000..7bad6dd --- /dev/null +++ b/src/apis/types/common_types.ts @@ -0,0 +1,4 @@ +/** + * Currency types, allowed values are NOK, DKK and EUR + */ +export type Currency = "NOK" | "DKK" | "EUR"; diff --git a/src/apis/types/ordermanagement_types.ts b/src/apis/types/ordermanagement_types.ts new file mode 100644 index 0000000..a445364 --- /dev/null +++ b/src/apis/types/ordermanagement_types.ts @@ -0,0 +1,218 @@ +import { Currency } from "./common_types.ts"; + +type EPAYMENT = "ecom"; +type ECOM = "ecom"; +type RECURRING = "recurring"; + +export type OrderManagementPaymentType = + | EPAYMENT + | ECOM + | RECURRING; + +type OrderManagementCategory = + | "GENERAL" + | "RECEIPT" + | "ORDER_CONFIRMATION" + | "DELIVERY" + | "TICKET" + | "BOOKING"; + +export type TypedOrderManagementAddCategoryToOrderRequest = { + /** + * The category of the order.
+ * GENERAL | RECEIPT | ORDER_CONFIRMATION | DELIVERY | TICKET | BOOKING + */ + category: OrderManagementCategory; // GENERAL | RECEIPT | ORDER_CONFIRMATION | DELIVERY | TICKET | BOOKING + /** + * The URL to the order details page on the merchant's website + */ + orderDetailsUrl: string; // URL + /** + * The ID of the image that is used to represent the on the merchants page + */ + imageId: string | null; // +}; + +export type OrderManagementAddCategoryOKResponse = { + string: string; +}; + +type InvalidParam = { + name: string; + reason: string; +}; + +export type OrderManagementErrorResponse = { + type?: string; + title: string; + detail: string; + instance: string; + invalidParams?: InvalidParam[]; +}; + +// /v1/images +export type OrderManagementAddImageRequest = { + imageId: string; + /** + * The image data as a base64 encoded string, not to be confused with a URL. + * The image _must_ be in PNG or JPEG format. Square images are recommended, min height is 167px and max file size is 2MB. + */ + src: string; + /** + * Value: base64 + */ + type: string; +}; + +export type OrderManagementAddImageOKResponse = { + imageId: string; +}; + +type OrderManagementQuantityUnit = + | "PCS" + | "KG" + | "KM" + | "MINUTE" + | "LITRE" + | "KWH"; + +type OrderManagementUnitInfo = { + /** + * The price per unit of the product in the order line. Must be in Minor Units. The smallest unit of a currency. Example 1,- NOK = 100. + * @format int64 + */ + unitPrice: number | null; + /** + * The quantity of the product in the order line. + * @maxLength 10 + */ + quantity: string; + /** + * Enum: "PCS" "KG" "KM" "MINUTE" "LITRE" "KWH" + * Available units for quantity. Will default to PCS if not set + * @default "PCS" + */ + quantityUnit?: OrderManagementQuantityUnit; +}; + +type OrderLine = { + /** + * The name of the product in the order line. + * @minLength 1 + * @maxLength 2048 + */ + name: string; + /** + * The product ID. + * TODO Verify that the maxLength is 255 characters + * @minLength 1 + * @maxLength 255 + */ + id: string; + /** + * Total amount of the order line, including tax and discount. Must be in Minor Units. The smallest unit of a currency. Example 100 NOK = 10000. + * @format int64 + * @min 0 + */ + totalAmount: number; + /** + * Total amount of order line with discount excluding tax. Must be in Minor Units. The smallest unit of a currency. Example 100 NOK = 10000. + * @format int64 + * @min 0 + */ + totalAmountExcludingTax: number; + /** + * Total tax amount paid for the order line. Must be in Minor Units. The smallest unit of a currency. Example 100 NOK = 10000. + * @format int64 + * @min 0 + */ + totalTaxAmount: number; + /** + * Tax percentage for the order line. + * @format int32 + * @min 0 + * @max 100 + */ + taxPercentage: number; + /** If no quantity info is provided the order line will default to 1 pcs. */ + unitInfo?: OrderManagementUnitInfo | null; + /** + * Total discount for the order line. Must be in Minor Units. The smallest unit of a currency. Example 100 NOK = 10000. + * @format int64 + */ + discount?: number | null; + /** URL linking back to the product at the merchant. */ + productUrl?: string | null; + /** Flag for marking the orderline as returned. This will make it count negative towards all the sums in BottomLine. */ + isReturn?: boolean | null; + /** Flag for marking the orderline as a shipping line. This will be shown differently in the app. */ + isShipping?: boolean | null; +}; + +type OMShippingInfo = { + amount: number | null; + amountExcludingTax: number | null; + taxAmount: number | null; + taxPercentage: number | null; +}; + +type OMPaymentSources = { + giftCard?: number | null; + card?: number | null; + voucher?: number | null; + cash?: number | null; +}; +export type BarcodeFormats = "EAN-13" | "CODE 39" | "CODE 128"; +type OMBarCode = { + format: string; + data: BarcodeFormats | null; +}; + +type BottomLine = { + /** + * The currency identifier according to ISO 4217. + * "NOK", "DKK" or "EUR. + * @minLength 3 + * @maxLength 3 + */ + currency: Currency; + /** + * Tip amount for the order. Must be in Minor Units. The smallest unit of a currency. Example 100 NOK = 10000. + * TODO Add validation that number is a positive integer + * @format int64 + */ + tipAmount?: number | null; + /** + * POS ID is the device number of the POS terminal. + * Not to be confused with any Vipps MobilePay ID. + */ + posId?: string | null; + + shippingInfo?: OMShippingInfo; + paymentSources?: OMPaymentSources; + barcode?: OMBarCode; + receiptNumber?: string | null; +}; + +// GET Order w/category and receipt +export type OrderManagementGetOrderOKResponse = { + category: OrderManagementCategory; + orderDetailsUrl: string; + imageId?: string | null; + + orderLines: OrderLine[]; + bottomLine: BottomLine; +}; + +// +// POST ADD RECEIPT +// +export type OrderManagementAddReceiptOKResponse = { + // AddReceipt returns text/plain string + body: string; +}; + +export type OrderManagementAddReceiptRequest = { + orderLines: OrderLine[]; + bottomLine: BottomLine; +}; diff --git a/src/mod.ts b/src/mod.ts index 903ba58..3ccb38b 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -12,6 +12,7 @@ import { redirectQRRequestFactory, } from "./apis/qr.ts"; import { loginRequestFactory } from "./apis/login.ts"; +import { orderManagementRequestFactory } from "./apis/ordermanagement.ts"; /** * Export all API types, for convenience. All exported types are @@ -24,6 +25,7 @@ export type * from "./apis/types/login_types.ts"; export type * from "./apis/types/qr_types.ts"; export type * from "./apis/types/recurring_types.ts"; export type * from "./apis/types/webhooks_types.ts"; +export type * from "./apis/types/ordermanagement_types.ts"; /** * Creates a client with the specified options. @@ -47,6 +49,7 @@ export const Client = (options: ClientConfig) => { }, redirectQR: createApi(client, redirectQRRequestFactory), webhook: createApi(client, webhooksRequestFactory), + orderManagement: createApi(client, orderManagementRequestFactory), } satisfies ApiClient; return apiClient;