11import { zodResolver } from "@hookform/resolvers/zod" ;
22import { X } from "lucide-react" ;
3- import { useEffect , useState } from "react" ;
3+ import { useEffect , useRef , useState } from "react" ;
44import { useForm } from "react-hook-form" ;
55import { redirect , useNavigation , useSubmit } from "react-router" ;
66import { z } from "zod" ;
@@ -22,6 +22,20 @@ import { commitSession, getSession } from "@/session.server";
2222
2323import type { Route } from "./+types" ;
2424
25+ interface CulqiInstance {
26+ open : ( ) => void ;
27+ close : ( ) => void ;
28+ token ?: { id : string } ;
29+ error ?: Error ;
30+ culqi ?: ( ) => void ;
31+ }
32+
33+ declare global {
34+ interface Window {
35+ CulqiCheckout : new ( publicKey : string , config : object ) => CulqiInstance ;
36+ }
37+ }
38+
2539const countryOptions = [
2640 { value : "AR" , label : "Argentina" } ,
2741 { value : "BO" , label : "Bolivia" } ,
@@ -69,6 +83,30 @@ export async function action({ request }: Route.ActionArgs) {
6983 const cartItems = JSON . parse (
7084 formData . get ( "cartItemsJson" ) as string
7185 ) as CartItem [ ] ;
86+ const token = formData . get ( "token" ) as string ;
87+
88+ const body = {
89+ amount : 2000 ,
90+ currency_code : "PEN" ,
91+ email : shippingDetails . email ,
92+ source_id : token ,
93+ capture : true ,
94+ } ;
95+
96+ const response = await fetch ( "https://api.culqi.com/v2/charges" , {
97+ method : "POST" ,
98+ headers : {
99+ "content-type" : "application/json" ,
100+ Authorization : `Bearer sk_test_EC8oOLd3ZiCTKqjN` ,
101+ } ,
102+ body : JSON . stringify ( body ) ,
103+ } ) ;
104+
105+ if ( ! response . ok ) {
106+ const errorData = await response . json ( ) ;
107+ console . error ( "Error creating charge:" , errorData ) ;
108+ throw new Error ( "Error processing payment" ) ;
109+ }
72110
73111 const items = cartItems . map ( ( item ) => ( {
74112 productId : item . product . id ,
@@ -117,12 +155,14 @@ export default function Checkout({ loaderData }: Route.ComponentProps) {
117155 const navigation = useNavigation ( ) ;
118156 const submit = useSubmit ( ) ;
119157 const loading = navigation . state === "submitting" ;
120- const [ Culqui , setCulqui ] = useState ( null ) ;
158+ const [ culqui , setCulqui ] = useState < CulqiInstance | null > ( null ) ;
159+ const scriptRef = useRef < HTMLScriptElement | null > ( null ) ;
121160
122161 const {
123162 register,
124163 handleSubmit,
125164 formState : { errors, isValid } ,
165+ getValues,
126166 } = useForm < CheckoutForm > ( {
127167 resolver : zodResolver ( CheckoutFormSchema ) ,
128168 defaultValues : {
@@ -141,93 +181,95 @@ export default function Checkout({ loaderData }: Route.ComponentProps) {
141181 } ) ;
142182
143183 useEffect ( ( ) => {
144- const script = document . createElement ( "script" ) ;
145- script . src = "https://js.culqi.com/checkout-js" ;
146- script . async = true ;
147- script . onload = ( ) => {
148- // Aqui ya existe window.CulqiCheckout
149- const settings = {
150- title : "FullStock" ,
151- currency : "USD" ,
152- amount : total * 100 , // Este parámetro es requerido para realizar pagos yape(80.00)
153- // order: "ord_live_d1P0Tu1n7Od4nZdp", // Este parámetro es requerido para realizar pagos con pagoEfectivo, billeteras y Cuotéalo
154- // xculqirsaid: "Inserta aquí el id de tu llave pública RSA",
155- // rsapublickey: "Inserta aquí tu llave pública RSA",
156- } ;
157-
158- const paymentMethods = {
159- // las opciones se ordenan según se configuren
160- tarjeta : true ,
161- yape : false ,
162- billetera : false ,
163- bancaMovil : false ,
164- agente : false ,
165- cuotealo : false ,
166- } ;
167-
168- const options = {
169- lang : "auto" ,
170- installments : true , // Habilitar o deshabilitar el campo de cuotas
171- modal : true ,
172- // container: "#culqi-container", // Opcional - Div donde quieres cargar el checkout
173- paymentMethods : paymentMethods ,
174- paymentMethodsSort : Object . keys ( paymentMethods ) , // las opciones se ordenan según se configuren en paymentMethods
175- } ;
176-
177- const appearance = {
178- theme : "default" ,
179- hiddenCulqiLogo : false ,
180- hiddenBannerContent : false ,
181- hiddenBanner : false ,
182- hiddenToolBarAmount : false ,
183- hiddenEmail : false ,
184- menuType : "select" , // sidebar / sliderTop / select
185- buttonCardPayText : "Pagar" , //
186- logo : null , // 'http://www.childrensociety.ms/wp-content/uploads/2019/11/MCS-Logo-2019-no-text.jpg',
187- defaultStyle : {
188- bannerColor : "blue" , // hexadecimal
189- buttonBackground : "yellow" , // hexadecimal
190- menuColor : "pink" , // hexadecimal
191- linksColor : "green" , // hexadecimal
192- buttonTextColor : "blue" , // hexadecimal
193- priceColor : "red" ,
194- } ,
195- } ;
196-
197- const config = {
198- settings,
199- // client,
200- options,
201- appearance,
202- } ;
203-
204- const publicKey = "pk_test_Ws4NXfH95QXlZgaz" ;
205- // @ts -ignore
206- const Culqi = new window . CulqiCheckout ( publicKey , config ) ;
207-
208- setCulqui ( Culqi ) ;
209-
210- console . log ( "Script loaded" ) ;
184+ // Function to load the Culqi script
185+ const loadCulqiScript = ( ) : Promise < Window [ "CulqiCheckout" ] > => {
186+ return new Promise < Window [ "CulqiCheckout" ] > ( ( resolve , reject ) => {
187+ if ( window . CulqiCheckout ) {
188+ resolve ( window . CulqiCheckout ) ;
189+ return ;
190+ }
191+
192+ // Create script element
193+ const script = document . createElement ( "script" ) ;
194+ script . src = "https://js.culqi.com/checkout-js" ;
195+ script . async = true ;
196+
197+ // Store reference for cleanup
198+ scriptRef . current = script ;
199+
200+ script . onload = ( ) => {
201+ if ( window . CulqiCheckout ) {
202+ resolve ( window . CulqiCheckout ) ;
203+ } else {
204+ reject (
205+ new Error (
206+ "Culqi script loaded but CulqiCheckout object not found"
207+ )
208+ ) ;
209+ }
210+ } ;
211+
212+ script . onerror = ( ) => {
213+ reject ( new Error ( "Failed to load CulqiCheckout script" ) ) ;
214+ } ;
215+
216+ document . head . appendChild ( script ) ;
217+ } ) ;
211218 } ;
212219
213- document . body . appendChild ( script ) ;
220+ loadCulqiScript ( )
221+ . then ( ( CulqiCheckout ) => {
222+ const config = {
223+ settings : {
224+ currency : "USD" ,
225+ amount : total * 100 ,
226+ } ,
227+ client : {
228+ email : user ?. email ,
229+ } ,
230+ options : { } ,
231+ appearance : { } ,
232+ } ;
233+
234+ const publicKey = "pk_test_Ws4NXfH95QXlZgaz" ;
235+ const culqiInstance = new CulqiCheckout ( publicKey , config ) ;
236+
237+ const handleCulqiAction = ( ) => {
238+ if ( culqiInstance . token ) {
239+ const token = culqiInstance . token . id ;
240+ culqiInstance . close ( ) ;
241+ const formData = getValues ( ) ;
242+ submit (
243+ {
244+ shippingDetailsJson : JSON . stringify ( formData ) ,
245+ cartItemsJson : JSON . stringify ( cart . items ) ,
246+ token,
247+ } ,
248+ { method : "POST" }
249+ ) ;
250+ } else {
251+ console . log ( "Error : " , culqiInstance . error ) ;
252+ }
253+ } ;
254+
255+ culqiInstance . culqi = handleCulqiAction ;
256+
257+ setCulqui ( culqiInstance ) ;
258+ } )
259+ . catch ( ( error ) => {
260+ console . error ( "Error loading Culqi script:" , error ) ;
261+ } ) ;
214262
215- // Cleanup function
216263 return ( ) => {
217- document . body . removeChild ( script ) ;
264+ if ( scriptRef . current ) {
265+ scriptRef . current . remove ( ) ;
266+ }
218267 } ;
219- } , [ ] ) ;
220-
221- async function onSubmit ( formData : CheckoutForm ) {
222- // submit(
223- // {
224- // shippingDetailsJson: JSON.stringify(formData),
225- // cartItemsJson: JSON.stringify(cart.items),
226- // },
227- // { method: "POST" }
228- // );
229- if ( Culqui ) {
230- Culqui . open ( ) ;
268+ } , [ total , user , submit , getValues , cart . items ] ) ;
269+
270+ async function onSubmit ( ) {
271+ if ( culqui ) {
272+ culqui . open ( ) ;
231273 }
232274 }
233275
@@ -353,6 +395,7 @@ export default function Checkout({ loaderData }: Route.ComponentProps) {
353395 </ Button >
354396 </ form >
355397 </ div >
398+ < div id = "culqi-container" > </ div >
356399 </ Container >
357400 </ Section >
358401 ) ;
0 commit comments