UNPKG

gapp-checkout

Version:

Mobile Gapp flow for Checkout

451 lines (392 loc) 19.6 kB
# Checkout GApp Flow GApp Flow for Checkout ## Installation ```sh npm install serino-mobile-gapp-checkout-flow --registry http://miniapp.serino.com:4873/ ``` ## Changelogs See the [Change logs](CHANGELOGS.md) ## Dependecies - npm install axios - npm install crypto-js ## (CheckoutGAppFlow) DataLoad Props | Prop | Required | Type | Description | | :------------------ | :------- | :------------ | :--------------------------------------------------------------------------------------------------- | | **`axiosOrder`** | No | AxiosInstance | set axios instance for order api. Applicable for dataloadType=`network-service` | | **`axiosCheckout`** | No | AxiosInstance | set axios instance for checkout api. Applicable for dataloadType=`network-service` | | **`endpoints`** | No | object | endpoint for checkout api. Applicable for dataloadType=`network-service` or `network-service-config` | | **`baseUrl`** | No | object | baseurl for getAll endpoint. Applicable for dataloadType=`network-service-config` | | **`payload`** | No | object | payload request. Applicable for dataloadType=`network-service` or `network-service-config` | | **`requestParams`** | No | object | Applicable for dataloadType=`network-service-config` | | **`cancelRequest`** | No | | Applicable for dataloadType=`network-service-config` | | **`timeout`** | No | | Applicable for dataloadType=`network-service-config` | | **`AppKey`** | No | | Applicable for dataloadType=`network-service-config` | | **`token`** | No | | Applicable for dataloadType=`network-service-config` | | **`tokenType`** | No | | Applicable for dataloadType=`network-service-config` | | **`header`** | No | object | Applicable for dataloadType=`network-service-config` | ## (CheckoutGAppFlow) DataIn Props | Prop | Required | Type | Description | | :----------------------------- | :------- | :----------------------------------------- | :------------------------------------------------------------------ | | **`dataLoadType`** | Yes | `network-service`,`network-service-config` | choices what dataLoad should be apply | | **`constructOrderPayload`** | Yes | function | function that reconstruct order payload. should return details. | | **`constructCheckoutPayload`** | No | function | function that reconstruct checkout payload. should return details. | | **`initialRouteName`** | No | string | initialRouteName for StackNavigator | | **`screens`** | No | array of objects | modify mini-app and gapp. able to use with the combination of Stack | ## (dataLoad > baseUrl) Props | Prop | Required | Type | Description | | :------------- | :------- | :----- | :-------------------------------------------------------------------------- | | **`order`** | No | string | base url for order. Applicable for dataloadType=`network-service-config` | | **`checkout`** | No | string | base url for checkout. Applicable for dataloadType=`network-service-config` | ## (dataLoad > endpoints) Props | Prop | Required | Type | Description | | :----------------- | :------- | :----- | :-------------------------------------------------------------------------- | | **`postOrder`** | No | string | endpoint for order. Applicable for dataloadType=`network-service-config` | | **`postCheckout`** | No | string | endpoint for checkout. Applicable for dataloadType=`network-service-config` | ## (dataLoad > payload) Props | Prop | Required | Type | Description | | :----------------------------------- | :------- | :-------------- | :----------------------------- | | **`payee`** | Yes | string | customer full name | | **`first_name`** | Yes | string | customer first name | | **`last_name`** | Yes | string | customer last name | | **`contact_number`** | Yes | string | customer contact number | | **`email`** | Yes | string | customer email | | **`region`** | Yes | string | region | | **`barangay`** | Yes | string | barangay | | **`street`** | Yes | string | street | | **`building_name`** | Yes | string | building name | | **`landmark`** | Yes | string | nearest landmark | | **`payment_method`** | Yes | string | payment method | | **`subtotal`** | Yes | number | order subtotal | | **`discount`** | Yes | number | voucher discount | | **`shopee_fee`** | Yes | number | shopee fee | | **`delivery_fee`** | Yes | number | delivery fee | | **`total_amount`** | Yes | number | total amount | | **`items`** | Yes | array of object | order items | | **`merchant_transaction_reference`** | Yes | string | merchant transaction reference | | **`merchant_processor_id`** | Yes | number | merchant processor id | | **`processor`** | Yes | object | processor | | any property name | No | any | the res payload values. | ## (dataLoad > payload > items) Props | Prop | Required | Type | Description | | :---------------- | :------- | :----- | :---------------------- | | **`name`** | Yes | string | product name | | **`description`** | Yes | string | product description | | **`quantity`** | Yes | number | product quantity | | **`price`** | Yes | number | prod' | | uct price | | any property name | No | any | the res payload values. | ## (dataLoad > payload > processor) Props | Prop | Required | Type | Description | | :----------------- | :------- | :----- | :--------------------- | | **`token`** | Yes | string | processor token | | **`redirect_url`** | Yes | string | processor redirect url | ## (dataLoad > payload > processor > redirect_url) Props | Prop | Required | Type | Description | | :------------ | :------- | :----- | :----------------------------- | | **`success`** | Yes | string | processor success redirect url | | **`failure`** | Yes | string | processor failure redirect url | | **`cancel`** | Yes | string | processor cancel redirect url | ## (dataIn > screens) Props | Prop | Required | Type | Description | | :----------------- | :------- | :------- | :----------------------------------------------------- | | **`key`** | No | string | mini-app key | | **`stackName`** | No | string | Stack name | | **`stackOptions`** | No | object | Stack options | | **`dataLoad`** | No | object | mini-app dataLoad. to modify initial and gapp dataLoad | | **`dataIn`** | No | object | mini-app dataIn. to modify initial and gapp dataIn | | **`dataOut`** | No | function | mini-app dataOut. to modify initial and gapp dataOut | ## useViewModel returned value ```bash 'customer-details':{ dataIn: { fullName, // payload > payee firstName, // payload > first_name lastName, // payload > last_name contactNumber, // payload > contact_number email, region, city, barangay, street, buildingName, // payload > building_name landmark, } }, 'order-items': { dataIn: { items, // order items }, }, 'payment-method': { dataIn: { label, // payload > payment_method; payment method label }, }, 'order-summary': { dataIn: { subTotal, // payload > subtotal voucherDiscount, // payload > discount shopperFee, // payload > shopee_fee deliveryFee, // payload > delivery_fee grandTotal, // payload > total_amount }, }, 'submit-order': { dataIn: { disabled, // true or false loading, // true or false }, dataOut, // function to apply order & checkout api } ``` ## Example Run the following commands `npm run boostrap`:setup project by installing all dependencies and pods. `npm run example run start`: start the Metro server for the example app. `npm run example run android`: run the example app on Android. `npm run example run ios`: run the example app on iOS. ## Example (dataLoadType="network-service") ### axiosCheckout.tsx ```jsx import axios, { AxiosResponse, AxiosError, AxiosRequestConfig } from 'axios'; const onRequest = (config: AxiosRequestConfig): AxiosRequestConfig => { const token = ''; config.headers.Authorization = `Bearer ${token}`; config.headers['x-public-key'] = ''; return config; }; const onRequestError = (error: AxiosError): Promise<AxiosError> => { return Promise.reject(error); }; const onResponse = (response: AxiosResponse) => { return response.data; }; const onResponseError = (error: AxiosError): Promise<AxiosError> => { return Promise.reject(error.response); }; const axiosCheckout = axios.create({ baseURL: 'https://....', }); axiosCheckout.interceptors.request.use(onRequest, onRequestError); axiosCheckout.interceptors.response.use(onResponse, onResponseError); export default axiosCheckout; ``` ### Checkout Screen ```jsx import * as React from 'react'; import { Button, Text } from 'react-native-paper'; import { CheckoutGAppFlow } from 'serino-mobile-gapp-checkout-flow'; import { GlobalContext } from '../context/GlobalContext'; import axiosCart from '../library/axiosCart'; import axiosCheckout from '../library/axiosCheckout'; import axiosOrder from '../library/axiosOrder'; import OrderItems from '../components/Cart/OrderedItems'; import OrderSummary from '../components/Cart/OrderSummary'; import checkoutJsonStab from '../json-stab/place-order copy.json'; const Checkout = ({ navigation }: any) => { const context: any = React.useContext(GlobalContext); const reconstructOrderPayload = (values: any) => { return { orders: [ { action_to_proceed: values.action_to_proceed || 'Cancel the whole order if any item is out of stock', cart_id: values.cart_id || 'a89b5c67-4c19-40e0-a81b-062523a078fb', change_for: values.total_amount || '0', customer: { contact_number: values.contact_number || '+639876546231', email: values.email || 'ej.dalman@serino.com', first_name: values.first_name || 'Ej', last_name: values.last_name || 'Dalman', reference_1: values.reference_1 || '', reference_2: values.reference_2 || '', reference_3: values.reference_3 || '', }, fulfillment_type: values.fulfillment_type || 'deliver later', items: values.items, merchant_reference_id: values.merchant_reference_id || 'market', notes: [{ type: 'Order Notes', notes: '' }], platform: 'Web', platform_details: values.platform_details || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36', promo_reference_id: '', reference_1: '', reference_2: '', reference_3: '', reservation_reference: 'branch_advance_6', schedule: '2023-02-28 10:30:0', shipping_address: { barangay: '', city: '', details: '', full_address: 'BLK 10 LOT 10 SAMPAGUITA STREET, Bel-Air, Makati, Metro Manila, Philippines', landmark: '', latitude: 14.554729, longitude: 121.0244452, pinned_address: 'Makati, Metro Manila, Philippines', region: '', street: '', zip_code: '', }, source: 'Central PH', store_reference_id: '6', }, ], payment: { type: values.payment_method_type, option: values.payment_method, }, }; }; const payload = { ...checkoutJsonStab, payment_method_type: context.paymentMethod?.parent_type || context.paymentMethod?.type, payment_method: context.paymentMethod?.text, merchant_processor_id: context.paymentMethod?.id, merchant_transaction_reference: 'CENTRAL03411', processor: { token: '', redirect_url: { success: 'https://ordering.com/CENTRAL03411/payment-result?status=sucess', failure: 'https://ordering.com/CENTRAL03411/payment-result?status=failure', cancel: 'https://ordering.com/CENTRAL03411/payment-result?status=cancel', }, }, items: context?.cartDetails.items.map((d: any) => { const customData = JSON.parse(d.custom_data); const barcodeDetails = customData ? customData.find((c: any) => c.name === 'barcode') : {}; return { barcode: barcodeDetails.value || '28400017503', description: d.itemDescription || '<p>Frito Lay it all start with farm grown potatoes cooked and seasoned to perfection, Then we add just the right balanced of tangly Vinegar. So every Lays potato is perfectly crispy and delicious happiness in every bite.</p>\n<ul>\n<li>No Artificial Flavours</li>\n<li>No Preservatives</li>\n</ul>\n<p><strong>Ingredients:</strong><br />Potatoes, Vegetable Oil (Sunflower, corn and/or Canola Oil), Salt &amp; Vinegar Seasoning (Maltodextrin made from corn, Natural Flavours, Salt, Malic Acid and Vinegar).</p>', image: d.itemImage || 'https://qa-centralmain.s3.ap-southeast-1.amazonaws.com/thumbnails/28400017503-01_8_thumbnail.jpg', instructions: d.instructions || '', name: d.itemName || 'Lays Salt & Vinegar Potato Chips 184.2g', options: '', price: d.price ? parseFloat(d.price) : 167.95, quantity: d.quantity || 1, reference_1: '', reference_2: '', reference_3: '', reference_id: d.itemReferenceId || '24698', sku: d.itemSKU || '105146', type: d.itemReferenceType || 'basic_product', weight: 0, weight_price_per_unit: 0, weight_uom: '', }; }), }; const public_key = 'pk-8c6a927a-972f-11ed-a8fc-0242ac120002'; const handleDataOut = (values: any) => { console.log('checkout DATAOUT', values); }; const fetchCart = React.useCallback(async () => { try { const res: any = await axiosCart.get('/cart'); context.setContext({ ...context, cartDetails: { ...context?.cartDetails, items: res?.data?.data, }, }); } catch (err: any) {} }, []); // life-cycle method to get cart details React.useEffect(() => { fetchCart(); }, []); return ( <React.Fragment> <CheckoutGAppFlow dataLoad={{ axiosCheckout: axiosCheckout, axiosOrder: axiosOrder, endpoints: { postOrder: '/public/create', postCheckout: `/api/public/merchants/${public_key}/checkout`, }, payload: payload, }} dataIn={{ dataLoadType: 'network-service', constructOrderPayload: reconstructOrderPayload, }} dataOut={handleDataOut} > <OrderItems key="order-items" dataIn={{ navigation }} /> <SelectPaymentMethod key="payment-method" dataIn={{ label: context.paymentMethod?.text ? context.paymentMethod.text : 'Select Payment Method', }} dataOut={() => { navigation.navigate('PaymentMethodFlow'); }} /> <OrderSummary key="order-summary" dataIn={{ subTotal: 0, voucherDiscount: 0, showPickupDiscount: false, pickupDiscount: 0, showShopperFee: false, shopperFee: 0, showDeliveryFee: false, deliveryFee: 0, }} /> <CheckoutButton key="submit-order" dataIn={{ label: 'Pay Now' }} dataOut={() => {}} /> </CheckoutGAppFlow> </React.Fragment> ); }; const CheckoutButton = (props: any) => { return ( <Button mode={props?.dataIn?.mode || 'contained'} disabled={props?.dataIn?.disabled || props?.dataIn?.loading} onPress={props?.dataOut} > {props?.dataIn?.loading ? 'Loading...' : props?.dataIn?.label} </Button> ); }; const SelectPaymentMethod = ({ dataIn, dataOut }: any) => { return ( <React.Fragment> <Text>Select Payment method</Text> <CheckoutButton dataIn={{ label: dataIn.label, mode: 'outlined', }} dataOut={dataOut} /> </React.Fragment> ); }; export default Checkout; ``` ## License MIT