Skip to content

Commit

Permalink
Merge pull request #405 from commercelayer/feat/improve-subscriptions
Browse files Browse the repository at this point in the history
Improve subscriptions and customer payment source support
  • Loading branch information
malessani authored Dec 13, 2023
2 parents ca2d7b1 + bf49ee8 commit 1acf132
Show file tree
Hide file tree
Showing 16 changed files with 344 additions and 31 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ workflows:
tags:
ignore: /v.*/

test-build-and-push:
build-and-push:
jobs:
- build:
context: commercelayer
Expand All @@ -130,7 +130,7 @@ workflows:
branches:
ignore: /.*/

test-build-and-push-on-pci-bucket:
build-and-push-on-pci-bucket:
jobs:
- build-on-pci-bucket:
context: commercelayer
Expand Down
2 changes: 1 addition & 1 deletion components/composite/Checkout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ const Checkout: React.FC<Props> = ({
<StepHeaderPayment step={getStepNumber("Payment")} />
}
>
<div className="mb-6">
<div>
<StepPayment />
</div>
</AccordionItem>
Expand Down
6 changes: 3 additions & 3 deletions components/composite/OrderSummary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ export const OrderSummary: React.FC<Props> = ({ appCtx, readonly }) => {
{!appCtx.hasShippingMethod
? t("orderRecap.notSet")
: props.priceCents === 0
? t("general.free")
: props.price}
? t("general.free")
: props.price}
</div>
</>
)
Expand Down Expand Up @@ -215,7 +215,7 @@ export const OrderSummary: React.FC<Props> = ({ appCtx, readonly }) => {
className="text-xl font-extrabold"
/>
</RecapLineTotal>
<ReturnToCart cartUrl={appCtx.cartUrl} />
{!appCtx.isComplete && <ReturnToCart cartUrl={appCtx.cartUrl} />}
</AmountWrapper>
</TotalWrapper>
</Wrapper>
Expand Down
3 changes: 3 additions & 0 deletions components/composite/StepCustomer/AddressInputGroup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface Props {
resource: TResource
required?: boolean
value?: string
pattern?: React.ComponentProps<typeof AddressInput>["pattern"]
openShippingAddress?: (props: ShippingToggleProps) => void
}

Expand All @@ -39,6 +40,7 @@ export const AddressInputGroup: React.FC<Props> = ({
resource,
required,
type,
pattern,
value,
openShippingAddress,
}) => {
Expand Down Expand Up @@ -157,6 +159,7 @@ export const AddressInputGroup: React.FC<Props> = ({
data-testid={`input_${fieldName}`}
name={fieldName}
type={type}
pattern={pattern}
value={valueStatus}
className="form-input"
/>
Expand Down
38 changes: 21 additions & 17 deletions components/composite/StepPayment/CheckoutCustomerPayment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface Props {
selectPayment: THandleClick
hasTitle: boolean
autoSelectCallback: () => void
hasSubscriptions: boolean
}

type TTemplateCustomerCards = Parameters<
Expand All @@ -33,6 +34,7 @@ export const CheckoutCustomerPayment: React.FC<Props> = ({
selectPayment,
hasTitle,
autoSelectCallback,
hasSubscriptions,
}) => {
const { t } = useTranslation()

Expand Down Expand Up @@ -74,23 +76,25 @@ export const CheckoutCustomerPayment: React.FC<Props> = ({
}

return (
<div className="flex items-center mt-4">
<WalletCheckbox
name={name}
id={name}
data-testid="save-to-wallet"
type="checkbox"
className="form-checkbox"
checked={checked}
onClick={handleClick}
onChange={handleChange}
/>
<Label
htmlFor={name}
dataTestId="payment-save-wallet"
textLabel={t("stepPayment.saveToWallet")}
/>
</div>
!hasSubscriptions && (
<div className="flex items-center mt-4">
<WalletCheckbox
name={name}
id={name}
data-testid="save-to-wallet"
type="checkbox"
className="form-checkbox"
checked={checked}
onClick={handleClick}
onChange={handleChange}
/>
<Label
htmlFor={name}
dataTestId="payment-save-wallet"
textLabel={t("stepPayment.saveToWallet")}
/>
</div>
)
)
}

Expand Down
13 changes: 8 additions & 5 deletions components/composite/StepPayment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,14 @@ export const StepPayment: React.FC = () => {
hasTitle={hasTitle}
/>
) : (
<CheckoutCustomerPayment
selectPayment={selectPayment}
autoSelectCallback={autoSelectCallback}
hasTitle={hasTitle}
/>
<>
<CheckoutCustomerPayment
selectPayment={selectPayment}
autoSelectCallback={autoSelectCallback}
hasTitle={hasTitle}
hasSubscriptions={appCtx.hasSubscriptions}
/>
</>
)
) : (
<p className="text-sm text-gray-400">
Expand Down
20 changes: 20 additions & 0 deletions components/composite/StepPlaceOrder/WarningIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import styled from "styled-components"
import tw from "twin.macro"

export const WarningIcon: React.FC = () => {
return (
<Svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
viewBox="0 0 256 256"
>
<path d="M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z"></path>{" "}
</Svg>
)
}

const Svg = styled.svg`
${tw`text-orange-400`}
`
26 changes: 26 additions & 0 deletions components/composite/StepPlaceOrder/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { Order } from "@commercelayer/sdk"
import { useContext, useState } from "react"
import { Trans, useTranslation } from "react-i18next"

import { RepeatIcon } from "../OrderSummary/RepeatIcon"

import { AppContext } from "components/data/AppProvider"
import { GTMContext } from "components/data/GTMProvider"
import { FlexContainer } from "components/ui/FlexContainer"
Expand All @@ -20,6 +22,7 @@ import {
StyledPrivacyAndTermsCheckbox,
PlaceOrderButtonWrapper,
} from "./styled"
import { WarningIcon } from "./WarningIcon"

interface Props {
isActive: boolean
Expand Down Expand Up @@ -65,6 +68,29 @@ const StepPlaceOrder: React.FC<Props> = ({

return (
<>
{appCtx.hasSubscriptions && isActive && (
<div
className={`text-gray-500 font-semibold p-4 m-5 mb-0 md:mb-5 md:mx-0 text-sm border border-dashed ${
!appCtx.isGuest ? "" : "border-orange-400"
}`}
>
{appCtx.isGuest ? (
<div className="flex">
<div className="relative w-4 mr-2 top-0.5">
<WarningIcon />
</div>
<p>{t("stepPayment.subscriptionWithoutCustomer")}</p>
</div>
) : (
<div className="flex">
<div className="relative w-4 mr-2 top-0.5">
<RepeatIcon />
</div>
<p>{t("stepPayment.subscriptionWithCustomer")}</p>
</div>
)}
</div>
)}
<ErrorsContainer data-testid="errors-container">
<StyledErrors
resource="orders"
Expand Down
1 change: 1 addition & 0 deletions components/data/AppProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const initialState: AppStateData = {
cartUrl: undefined,
taxIncluded: false,
shippingMethodName: undefined,
hasSubscriptions: false,
}

export const AppContext = createContext<AppProviderData | null>(null)
Expand Down
16 changes: 16 additions & 0 deletions components/data/AppProvider/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export interface FetchOrderByIdResponse {
isCreditCard: boolean
taxIncluded: NullableType<boolean>
shippingMethodName?: string
hasSubscriptions: boolean
}

function isNewAddress({
Expand Down Expand Up @@ -262,10 +263,12 @@ export const fetchOrder = (cl: CommerceLayerClient, orderId: string) => {
"payment_method",
"payment_source",
"customer",
"line_items",
],
shipments: ["shipping_method", "available_shipping_methods"],
customer: ["customer_addresses"],
customer_addresses: ["address"],
line_items: ["frequency"],
},
include: [
"shipping_address",
Expand All @@ -278,6 +281,7 @@ export const fetchOrder = (cl: CommerceLayerClient, orderId: string) => {
"customer",
"customer.customer_addresses",
"customer.customer_addresses.address",
"line_items",
],
})
}
Expand Down Expand Up @@ -330,6 +334,17 @@ export function calculateSettings(
order.customer?.customer_addresses || customerAddress
)

const hasSubscriptions =
order.line_items?.some((item) => {
return item.frequency && item.frequency?.length > 0
}) ||
order.subscription_created_at !== null ||
false

if (hasSubscriptions && !isGuest) {
localStorage.setItem("_save_payment_source_to_customer_wallet", "true")
}

return {
isGuest,
shippingCountryCodeLock: order.shipping_country_code_lock,
Expand All @@ -347,6 +362,7 @@ export function calculateSettings(
cartUrl: order.cart_url,
taxIncluded: order.tax_included,
requiresBillingInfo: order.requires_billing_info,
hasSubscriptions,
}
}

Expand Down
4 changes: 3 additions & 1 deletion public/static/locales/de/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@
"notRequired": "Diese Bestellung erfordert keine Zahlung",
"amountZero": "Der Gesamtbetrag der Bestellung ist Null. Du kannst die Bestellung so aufgeben, wie sie jetzt ist",
"saveToWallet": "Speichere eine neue Karte",
"expires": "Ablaufdatum"
"expires": "Ablaufdatum",
"subscriptionWithoutCustomer": "An account must be created in order to purchase your subscription.",
"subscriptionWithCustomer": "By providing your card information, you allow the company to charge your card for future payments in accordance with their terms."
},

"addressForm": {
Expand Down
4 changes: 3 additions & 1 deletion public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@
"notRequired": "This order does not require payment",
"amountZero": "The total amount of the order is zero, you can place the order as it is right now",
"saveToWallet": "Save new card on your wallet",
"expires": "Expires"
"expires": "Expires",
"subscriptionWithoutCustomer": "An account must be created in order to purchase your subscription.",
"subscriptionWithCustomer": "By providing your card information, you allow the company to charge your card for future payments in accordance with their terms."
},

"addressForm" : {
Expand Down
4 changes: 3 additions & 1 deletion public/static/locales/it/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@
"notRequired": "Questo ordine non necessita di pagamento",
"amountZero": "L'importo totale dell'ordine è zero, puoi effettuare l'ordine così com'è adesso",
"saveToWallet": "Salva la nuova carta sul tuo portafoglio",
"expires": "Scadenza"
"expires": "Scadenza",
"subscriptionWithoutCustomer": "Crea un account prima di procedere con il tuo acquisto periodico.",
"subscriptionWithCustomer": "Ciascun ordine periodico verrà addebitato sulla carta fornita, come indicato nelle condizioni di vendita."
},

"addressForm" : {
Expand Down
Loading

0 comments on commit 1acf132

Please sign in to comment.