-
Notifications
You must be signed in to change notification settings - Fork 289
feat(payment): POC headless wallet buttons implementation #1999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: canary
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
4 Skipped Deployments
|
I'd like to provide some information:
Based on this PR will be created appropriate tickets when |
Hey @bc-nick are we going to copy paste all of sdk dist file code here? Can we otherwise fetch the SDK button script from CDN? |
@animesh1987 it will be enough to copy paste only required files for |
if (request.url.includes('/cart-information') && request.method === 'GET') { | ||
const cartId = request.nextUrl.searchParams.get('cartId'); | ||
|
||
if (cartId) { | ||
try { | ||
const cart = await fetchCart(cartId); | ||
|
||
return NextResponse.json(cart); | ||
} catch (error) { | ||
return NextResponse.json({ error }); | ||
} | ||
} | ||
|
||
return NextResponse.json( | ||
{ error: 'Invalid request body: cartId is not provided' }, | ||
{ status: 400 }, | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After chatting offline, we should remove this entire middleware handler and move these blocks into route handlers.
For example, this block should be moved to core/app/api/(wallet-buttons)/cart-information/route.ts
:
import { NextRequest, NextResponse } from 'next/server';
export const GET = async (request: NextRequest) => {
const cartId = request.nextUrl.searchParams.get('cartId');
if (cartId) {
try {
const cart = await fetchCart(cartId);
return NextResponse.json(cart);
} catch (error) {
return NextResponse.json({ error });
}
}
return NextResponse.json(
{ error: 'Invalid request body: cartId is not provided' },
{ status: 400 },
);
};
{walletButtons && ( | ||
<div className="mt-4"> | ||
<ClientWalletButtons walletButtons={walletButtons} /> | ||
</div> | ||
)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@migueloller @hunterbecton, do you think this is the right API to consume the wallet buttons in Vibes?
core/public/v1/loader.js
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As chatted about offline, these files are only for local development only. We will not need these files until bigcommerce/checkout-sdk-js#2742 is merged and deployed to the CDN. Leaving this comment to ensure we remove these files before merge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I will remove this files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going to comment on all the little changes I wanted in this core/lib/wallet-buttons
but it got pretty long. Instead I have a branch of my vision of what I wanted to change PAYPAL-5114...chore/headless-wallets-cleanup
The main subjects:
- Reduce the amount of indirections (too many files) to complete the task.
- Passed the only information needed into the initializer (
methodIds
) to reduce surface area
- Passed the only information needed into the initializer (
- Simplify the
WalletButtonsInitializer
class and remove any utilities + simplify types.- Only thing needed by the constructor was the url.
- Moved the loader script URL into a private attribute.
- Add a union of types for the different payment options. In practice, this usually helps remove a lot of conditional type checking in application code.
- Reject initializing if the script element fails
onerror
+onabort
. - Proper gql.tada usage, instead of using string template literals to inject dynamic data.
- Removed additions to
core/client/queries
– when you remove items from middleware, just place the fetching in the same file as the route handlers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the recommendations, I will take a look tomorrow and come back as soon as possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have made some updates regarding comments related to most of them
const graphQLQuery = graphql(` | ||
query { | ||
site { | ||
paymentWalletWithInitializationData(filter: {paymentWalletEntityId: "${entityId}"}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the proper way to pass in data in gql.tada queries, check around the codebase to see how to do it properly or check out this example PAYPAL-5114...chore/headless-wallets-cleanup#diff-d5e5c3bd2d9602afaf35db11c6adea51a6a29268df218fe0e76e3035a31d57c5R200-R227.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you check out the docs and experiment on how to properly add route handlers. I want to completely get rid of this middleware and route handlers solve that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This server component file is not even being used. Let's just move all the code from core/components/wallet-buttons/_components/client-wallet-buttons/index.tsx
into this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is not used yet. But I left it in order to have opportunity render this component through server side. But I can remove it of course
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since checkout-sdk is client-side only, it doesn't really make sense to keep a server component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not really a server action. Like in my example, can we just move this into page-data.ts
and/or inline some of this in the page.tsx
file (see my branch).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add this changes and let you know, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these additional queries that were added can just be inlined inside the route handlers once you have them set up correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got you, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not all of them. This request will not be moved because it is using in another place.
I have removed middleware and switched to route api.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chanceaclark do we really need to move all queries to the route handlers? It we do, I would like to suggest create a file for each of them app/api/wallet-buttons/.../query.ts
to avoid a huge amount of code accumulating in one place. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just inline in each of the route handlers for now, we can optimize/move them later if it deems necessary.
|
||
const cart = response.data.site.cart; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change seems unnecessary. Can we just destructure here instead of splitting the function in two separate functions? I understand you are trying to fetch the cart separately, but it looks like you just need the carts currency code. I would just create a separate query for the currency code, more inlined with the route handler that needs it.
const { cart, checkout } = response.data.site;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did we need to change this file? Seems like something we should avoid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we do not need to work with form validation and with cart action in our case. We need to make a request for getting information about redirect_urls only and pass it to checkout-sdk.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd avoid changing the original function and make a separate function for what you need. Abstracting things is not always the best approach and it's fine to be duplicative at first.
core/lib/wallet-buttons/utils.ts
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason why my branch didn't have this util file is that it's a bit of a indirection. We should keep the API of WalletButtonsInitializer
pretty slim, which means that it only needs to know about the methodIds
that are needed. We can return the Option
interface to the consumer to handle what it needs to.
The indirections I see:
-> fetch methods (app)
-> getWalletButtonOptions (lib)
-> pass to wallet buttons component (app)
-> return options (lib)
-> render components (app)
However, we can simplify this:
-> fetch methods (app)
-> pass to wallet buttons component (app)
-> initialize options (lib)
-> render components (app)
It also means we don't have to handle the undefined
case in our app, which adds complexity. We should handle this inside the lib and only return the type Option[]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately this filter will not work correctly because the id's are different. But I think you are right about your approach. I will make some updates providing approach based on your's suggested and will let you know
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you have I think is way more complex than it should be. If the id's are different you can just change this data structure to be a record instead, then your initialize
function returns an array:
const options: Record<string, Option> = {
'paypalcommerce.paypal': {
methodId: 'paypalcommerce',
containerId: 'paypalcommerce-button',
paypalcommerce: {
style: { color: 'gold', label: 'checkout' },
cartId: 'cartId',
},
},
// ...
};
// ...
async initialize(methodIds: string[]): Promise<Option[]> {
await this.initializeCheckoutKitLoader();
const checkoutButtonInitializer = await this.initCheckoutButtonInitializer();
return methodIds
.map(methodId => options[methodId])
.filter(Boolean)
.map((option) => {
checkoutButtonInitializer.initializeHeadlessButton(option);
return option;
});
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chanceaclark I created class that works with that structure and return config with available wallet button options. This options may be different for each of the available buttons.
⚡️🏠 Lighthouse reportLighthouse ran against https://catalyst-canary-fggvp8r1u-bigcommerce-platform.vercel.app 🖥️ DesktopWe ran Lighthouse against the changes on a desktop and produced this report. Here's the summary:
📱 MobileWe ran Lighthouse against the changes on a mobile and produced this report. Here's the summary:
|
What/Why?
POC of Headless Wallet Buttons feature
POC related to checkout-sdk part: bigcommerce/checkout-sdk-js#2742
Testing
headless_wallet_buttons_with_catalyst_sf.mov