export async function getProducts() {
 const { catalogApi } = client;
 try {
   const { result: { objects, relatedObjects }} = await catalogApi.searchCatalogObjects({
     objectTypes: [
       'ITEM'
     ],
     includeRelatedObjects: true
   });

   const images = relatedObjects.filter(object => object.type === 'IMAGE');
   const items = objects.filter(object => object.type === 'ITEM');

   const products = items.map(object => {
     const {
       id,
       imageId,
       itemData: {
         name,
         description,
         variations,
         categoryIds,
       }
     } = object;
     const imageUrl = images.find(image => image.id === imageId).imageData?.url;

     return {
       id,
       name,
       description,
       imageUrl,
       variations,
       categoryIds
     }
   })
   return JSON.parse(JSON.stringify(products));
 } catch (e) {
   console.error(e);
   throw e;
 }
}

import { formatCurrency } from "../utils/formatting";
import Link from 'next/link'

export default function ProductList({ products }) {
 return (products.length !== 0) ? (
   <div className="bg-white">
     <div className="max-w-2xl mx-auto py-16 px-4 sm:py-24 sm:px-6 lg:max-w-7xl lg:px-8">
       <h2 className="sr-only">Products</h2>

       <div className="grid grid-cols-1 gap-y-10 sm:grid-cols-2 gap-x-6 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
         {products.map((product) => (
           <div key={product.variations[0].id} className="group">
             <Link href={product.id} className="group">
               <div className="w-full aspect-w-1 aspect-h-1 bg-gray-200 rounded-lg overflow-hidden xl:aspect-w-7 xl:aspect-h-8">
                 <img
                   src={product.imageUrl}
                   alt={product.description}
                   className="w-full h-full object-center object-cover group-hover:opacity-75"
                 />
               </div>
             </Link>
             <h3 className="mt-4 text-sm text-gray-700">{product.name}</h3>
             <p className="mt-1 text-lg font-medium text-gray-900">{formatCurrency(product.variations[0].itemVariationData.priceMoney.amount)}</p>
           </div>
         ))}
       </div>
     </div>
   </div>
 ) : <span>Loading</span>
}

import React, {useEffect, useState} from 'react';
import {Image, Text, TouchableOpacity, View} from 'react-native';
import tailwind from 'tailwind-rn';
import {formatCurrency} from '../../utils/utils';

export default function ProductList({navigation, route}) {
 const [products, setProducts] = useState([]);

 useEffect(() => {
   fetchProducts();
 }, []);

 const fetchProducts = async () => {
   const res = await fetch('http://localhost:3000/api/square/catalog');
   const catalogProducts = await res.json();
   console.log(catalogProducts);
   setProducts(catalogProducts);
 };

 return products.length !== 0 ? (
   <View style={tailwind('bg-white')}>
     <View style={tailwind('max-w-2xl py-16 px-4')}>
       <View style={tailwind('flex')}>
         {products.map(product => {
           return (
             <View key={product.id} style={tailwind('')}>
               <TouchableOpacity
                 style={tailwind('w-full rounded-lg overflow-hidden')}
                 onPress={() => {
                   navigation.navigate('Checkout', {
                     product,
                   });
                 }}>
                 <Image
                   source={{
                     uri: product.imageUrl,
                   }}
                   alt={product.description}
                   style={tailwind('w-full h-64')}
                 />
               </TouchableOpacity>
               <Text style={tailwind('mt-4 text-sm text-gray-700')}>
                 {product.name}
               </Text>
               <Text
                 style={tailwind('mt-1 text-lg font-medium text-gray-900')}>
                 {formatCurrency(
                   product.variations[0].itemVariationData.priceMoney.amount,
                 )}
               </Text>
             </View>
           );
         })}
       </View>
     </View>
   </View>
 ) : (
   <Text>Loading</Text>
 );
}

import React from 'react';
import {Platform, Text, TouchableOpacity} from 'react-native';
import {SQIPCardEntry} from 'react-native-square-in-app-payments';

const iOSCardEntryTheme = {
 saveButtonFont: {
   size: 30,
 },
 saveButtonTitle: 'Pay',
 keyboardAppearance: 'Light',
};

export function SquarePay({
 textStyle,
 buttonStyle,
 buttonText,
 product,
 customer,
}) {
 function startCardFlow() {
   const cardEntryConfig = {
     collectPostalCode: false,
   };
   if (Platform.OS === 'ios') {
     SQIPCardEntry.setIOSCardEntryTheme({
       ...iOSCardEntryTheme,
     });
   }
   SQIPCardEntry.startCardEntryFlow(
     cardEntryConfig,
     onCardNonceRequestSuccess,
     onCardEntryCancel,
   ).then();
 }

 function onCardEntryComplete() {
   // mettre à jour l’interface pour notifier l'utilisateur que le paiement a été complété avec succès.
 }

 function onCardEntryCancel() {
   // Manage the callback d’annulation
 }

 async function onCardNonceRequestSuccess(cardDetails) {
   try {
     // prendre le paiement avec les détails de la carte
     // attendre chargeCard(cardDetails);
     const resp = await fetch('http://localhost:3000/api/square/pay', {
       method: 'POST',
       headers: {
         'Content-Type': 'application/json',
       },
       body: JSON.stringify({
         locationId: '9PKBD3JCQFH0D',
         sourceId: cardDetails.nonce,
         customer: {
           ...customer,
         },
         order: [
           {
             itemId: product.variations[0].id,
             quantity: '1',
           },
         ],
       }),
     });
     const data = await resp.json();
     console.info(data);
     // Paiement complet
     // vous devez appeler cette méthode pour compléter l'entrée de la carte
     await SQIPCardEntry.completeCardEntry(onCardEntryComplete());
   } catch (ex) {
     // Le paiement n’a pas été complète à cause d’une erreur
     // Envoyer une notification pour avertir d’une erreur
     await SQIPCardEntry.showCardNonceProcessingError(ex.message);
   }
 }

 return (
   <TouchableOpacity style={buttonStyle} onPress={startCardFlow}>
     <Text style={textStyle}>{buttonText}</Text>
   </TouchableOpacity>
 );
}

<SquarePaymentsForm
 applicationId="sandbox-sq0idb-B-i4MbEZEARTWqSj7Gs9Ww"
 locationId="9PKBD3JCQFH0D"
 cardTokenizeResponseReceived={async (token, buyer) => {
   if (token.details.method === "Google Pay") {
     setCustomer({
       ...token.details.billing,
     });
   }
   const resp = await fetch("/api/square/pay", {
     method: "POST",
     headers: {
       "Content-Type": "application/json",
     },
     body: JSON.stringify({
       locationId: "9PKBD3JCQFH0D",
       sourceId: token.token,
       customer: {
         ...customer,
       },
       order: [
         {
           itemId: product.variations[0].id,
           quantity: "1",
         },
       ],
     }),
   });
   const data = await resp.json();
   console.info(data);
   router.push({
     pathname: "/checkout/success",
     query: {
       customerId: data.customer.id,
       orderId: data.order.id,
     },
   });
 }}
 createPaymentRequest={() => ({
   countryCode: "US",
   currencyCode: "USD",
   requestBillingContact: true,
   total: {
     amount: (
       product.variations[0].itemVariationData.priceMoney.amount / 100
     ).toString(),
     label: "Total",
   },
 })}
>
 <CreditCardInput />
 <GooglePay />
 <ApplePay />
</SquarePaymentsForm>;

export default async function handler(req, res) {
 if (req.method === 'POST') {
   console.log(req.body);

   const { customer, sourceId, order, locationId } = req.body;
   try {
     const { result: {customer: newCustomer} } = await customersApi.createCustomer(customer);
     const { result: {order: newOrder} } = await ordersApi.createOrder({
       idempotencyKey: randomUUID(),
       order: {
         locationId,
         customerId: newCustomer.id,
         lineItems: order.map(({ itemId, quantity }) => {
           return {
             catalogObjectId: itemId,
             quantity,
           }
         }),
         fulfillments: [
           {
             type: 'PICKUP',
             pickupDetails: {
               recipient: {
                 customerId: newCustomer.id,
               },
               pickupAt: new Date(Date.now()).toISOString(),
               scheduleType: 'ASAP',
             }
           }
         ]
       }
     });
     const { result: {payment: newPayment} } = await paymentsApi.createPayment({
       idempotencyKey: randomUUID(),
       amountMoney: {
         amount: newOrder.totalMoney.amount,
         currency: "USD",
       },
       sourceId,
       orderId: newOrder.id,
       customerId: newCustomer.id,
       autocomplete: true,
       locationId,
     });

     if ( newPayment.status === 'COMPLETED' ) {
       res.status(200).json({
         customer: newCustomer,
         order: newOrder,
         payment: newPayment,
       });
     } else {
       res.status(402).json({
         message: 'Payment failed',
       });
     }
   } catch (error) {
     console.log(error);
     res.status(500).send(error);
   }

   // res.status(200).json(req.body);
 } else {
   res.status(405).json({
     error: 'Method not allowed',
   });
 }
};

try {
 const response = await client.devices.create_device_code(body: {
   idempotency_key: '{{CHAÎNE_ALÉATOIRE}}',
   device_code: {
     name: 'Comptoir 1',
     product_type: 'TERMINAL_API',
     location_id: 'B5E4484SHHNYH',
   }
 });

 console.log(response.result);
} catch (error) {
 console.log(error);
}

try {
 const response = await client.terminalApi.createTerminalCheckout({
   idempotencyKey: '{{CHAÎNE_ALÉATOIRE}}',
   checkout: {
     amountMoney: {
       amount: 100,
       currency: 'EUR'
     },
     note: 'une note brève',
     deviceOptions: {
       deviceId: 'R5WNWB5BKNG9R',
       skipReceiptScreen: false,
       tipSettings: {
         allowTipping: true,
         separateTipScreen: true,
         customTipField: false
       }
     }
   }
 });

 console.log(response.result);
} catch (error) {
 console.log(error);
}