From 5f47624d4e0d4b66c8fa6005344118f7ceb5911e Mon Sep 17 00:00:00 2001 From: Sylvain Date: Thu, 29 Apr 2021 17:10:02 +0200 Subject: [PATCH] WIP: create payment schedule w/ payzen --- app/controllers/api/payzen_controller.rb | 5 +++ app/frontend/src/javascript/api/payzen.ts | 15 +++++++- .../components/payment/payzen/payzen-form.tsx | 38 ++++++++++++++++--- app/frontend/src/javascript/models/payzen.ts | 4 +- .../api/payzen/create_token.json.jbuilder | 4 ++ config/routes.rb | 1 + 6 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 app/views/api/payzen/create_token.json.jbuilder diff --git a/app/controllers/api/payzen_controller.rb b/app/controllers/api/payzen_controller.rb index 555a3693f..cc7ff5e8b 100644 --- a/app/controllers/api/payzen_controller.rb +++ b/app/controllers/api/payzen_controller.rb @@ -61,6 +61,11 @@ class API::PayzenController < API::PaymentsController render json: e, status: :unprocessable_entity end + def confirm_payment_schedule + # TODO + raise NotImplementedError + end + private def on_reservation_success(order_id, details) diff --git a/app/frontend/src/javascript/api/payzen.ts b/app/frontend/src/javascript/api/payzen.ts index 228f7e457..a5e684f89 100644 --- a/app/frontend/src/javascript/api/payzen.ts +++ b/app/frontend/src/javascript/api/payzen.ts @@ -2,7 +2,13 @@ import apiClient from './clients/api-client'; import { AxiosResponse } from 'axios'; import { CartItems } from '../models/payment'; import { User } from '../models/user'; -import { CheckHashResponse, ConfirmPaymentResponse, CreatePaymentResponse, SdkTestResponse } from '../models/payzen'; +import { + CheckHashResponse, + ConfirmPaymentResponse, + CreatePaymentResponse, + CreateTokenResponse, + SdkTestResponse +} from '../models/payzen'; export default class PayzenAPI { @@ -16,7 +22,7 @@ export default class PayzenAPI { return res?.data; } - static async chargeCreateToken(cartItems: CartItems, customer: User): Promise { + static async chargeCreateToken(cartItems: CartItems, customer: User): Promise { const res: AxiosResponse = await apiClient.post('/api/payzen/create_token', { cart_items: cartItems, customer_id: customer.id }); return res?.data; } @@ -30,4 +36,9 @@ export default class PayzenAPI { const res: AxiosResponse = await apiClient.post('/api/payzen/confirm_payment', { cart_items: cartItems, order_id: orderId }); return res?.data; } + + static async confirmSchedule(orderId: string, cartItems: CartItems): Promise { + const res: AxiosResponse = await apiClient.post('/api/payzen/confirm_payment_schedule', { cart_items: cartItems, order_id: orderId }); + return res?.data; + } } diff --git a/app/frontend/src/javascript/components/payment/payzen/payzen-form.tsx b/app/frontend/src/javascript/components/payment/payzen/payzen-form.tsx index 5539c11ef..77cb37aca 100644 --- a/app/frontend/src/javascript/components/payment/payzen/payzen-form.tsx +++ b/app/frontend/src/javascript/components/payment/payzen/payzen-form.tsx @@ -1,12 +1,16 @@ import React, { FormEvent, FunctionComponent, useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; import KRGlue from "@lyracom/embedded-form-glue"; import { GatewayFormProps } from '../abstract-payment-modal'; import SettingAPI from '../../../api/setting'; import { SettingName } from '../../../models/setting'; import PayzenAPI from '../../../api/payzen'; import { Loader } from '../../base/loader'; -import { KryptonClient, KryptonError, ProcessPaymentAnswer } from '../../../models/payzen'; +import { + CreateTokenResponse, + KryptonClient, + KryptonError, + ProcessPaymentAnswer +} from '../../../models/payzen'; /** * A form component to collect the credit card details and to create the payment method on Stripe. @@ -14,14 +18,13 @@ import { KryptonClient, KryptonError, ProcessPaymentAnswer } from '../../../mode */ export const PayzenForm: React.FC = ({ onSubmit, onSuccess, onError, children, className, paymentSchedule = false, cartItems, customer, operator, formId }) => { - const { t } = useTranslation('shared'); const PayZenKR = useRef(null); const [loadingClass, setLoadingClass] = useState<'hidden' | 'loader' | 'loader-overlay'>('loader'); useEffect(() => { const api = new SettingAPI(); api.query([SettingName.PayZenEndpoint, SettingName.PayZenPublicKey]).then(settings => { - PayzenAPI.chargeCreatePayment(cartItems, customer).then(formToken => { + createToken().then(formToken => { // Load the remote library KRGlue.loadLibrary(settings.get(SettingName.PayZenEndpoint), settings.get(SettingName.PayZenPublicKey)) .then(({ KR }) => @@ -38,6 +41,18 @@ export const PayzenForm: React.FC = ({ onSubmit, onSuccess, on }); }, [cartItems, paymentSchedule, customer]); + /** + * Ask the API to create the form token. + * Depending on the current transaction (schedule or not), a PayZen Token or Payment may be created. + */ + const createToken = async (): Promise => { + if (paymentSchedule) { + return await PayzenAPI.chargeCreateToken(cartItems, customer); + } else { + return await PayzenAPI.chargeCreatePayment(cartItems, customer); + } + } + /** * Callback triggered on PayZen successful payments * @see https://docs.lyra.com/fr/rest/V4.0/javascript/features/reference.html#kronsubmit @@ -48,7 +63,7 @@ export const PayzenForm: React.FC = ({ onSubmit, onSuccess, on const transaction = event.clientAnswer.transactions[0]; if (event.clientAnswer.orderStatus === 'PAID') { - PayzenAPI.confirm(event.clientAnswer.orderDetails.orderId, cartItems).then((confirmation) => { + confirm(event).then((confirmation) => { PayZenKR.current.removeForms().then(() => { onSuccess(confirmation); }); @@ -58,10 +73,21 @@ export const PayzenForm: React.FC = ({ onSubmit, onSuccess, on onError(error || event.clientAnswer.orderStatus); } } - }) + }); return true; }; + /** + * Ask the API to confirm the processed transaction, depending on the current transaction (schedule or not). + */ + const confirm = async (paymentAnswer: ProcessPaymentAnswer): Promise => { + if (paymentSchedule) { + return await PayzenAPI.confirm(paymentAnswer.clientAnswer.orderDetails.orderId, cartItems); + } else { + return await PayzenAPI.confirmSchedule(paymentAnswer.clientAnswer.orderDetails.orderId, cartItems); + } + } + /** * Callback triggered when the PayZen form was entirely loaded and displayed * @see https://docs.lyra.com/fr/rest/V4.0/javascript/features/reference.html#%C3%89v%C3%A9nements diff --git a/app/frontend/src/javascript/models/payzen.ts b/app/frontend/src/javascript/models/payzen.ts index 4d2ac8990..b28059d01 100644 --- a/app/frontend/src/javascript/models/payzen.ts +++ b/app/frontend/src/javascript/models/payzen.ts @@ -2,11 +2,13 @@ export interface SdkTestResponse { success: boolean } -export interface CreatePaymentResponse { +export interface CreateTokenResponse { formToken: string orderId: string } +export interface CreatePaymentResponse extends CreateTokenResponse {} + export interface ConfirmPaymentResponse { todo?: any } diff --git a/app/views/api/payzen/create_token.json.jbuilder b/app/views/api/payzen/create_token.json.jbuilder new file mode 100644 index 000000000..39ceb1ced --- /dev/null +++ b/app/views/api/payzen/create_token.json.jbuilder @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +json.formToken @result['answer']['formToken'] +json.orderId @id diff --git a/config/routes.rb b/config/routes.rb index b8ba23519..f2189ee99 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -185,6 +185,7 @@ Rails.application.routes.draw do post 'payzen/confirm_payment' => 'payzen#confirm_payment' post 'payzen/check_hash' => 'payzen#check_hash' post 'payzen/create_token' => 'payzen#create_token' + post 'payzen/confirm_payment_schedule' => 'payzen#confirm_payment_schedule' # FabAnalytics get 'analytics/data' => 'analytics#data'