Skip to content

Commit da7491a

Browse files
authored
feat: improved funding flow in Checkout (#1692)
1 parent 163efaf commit da7491a

6 files changed

+28
-88
lines changed

.changeset/cold-clocks-sip.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@coinbase/onchainkit': patch
3+
---
4+
5+
feat: improved funding flow in Checkout by @0xAlec #1692

src/checkout/components/CheckoutButton.test.tsx

-12
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,6 @@ describe('CheckoutButton', () => {
5757
expect(screen.getByRole('button').textContent).toBe('View payment details');
5858
});
5959

60-
it('should render "Get USDC" when there is insufficient balance error', () => {
61-
useCheckoutContextMock.mockReturnValue({
62-
lifecycleStatus: {
63-
statusName: CHECKOUT_LIFECYCLESTATUS.ERROR,
64-
statusData: { error: 'User has insufficient balance' },
65-
},
66-
onSubmit: mockOnSubmit,
67-
});
68-
render(<CheckoutButton />);
69-
expect(screen.getByRole('button').textContent).toBe('Get USDC');
70-
});
71-
7260
it('should call onSubmit when clicked', () => {
7361
render(<CheckoutButton />);
7462
fireEvent.click(screen.getByRole('button'));

src/checkout/components/CheckoutButton.tsx

+1-7
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,8 @@ export function CheckoutButton({
3535
if (lifecycleStatus?.statusName === CHECKOUT_LIFECYCLESTATUS.SUCCESS) {
3636
return 'View payment details';
3737
}
38-
if (
39-
lifecycleStatus?.statusName === CHECKOUT_LIFECYCLESTATUS.ERROR &&
40-
lifecycleStatus?.statusData.error === 'User has insufficient balance'
41-
) {
42-
return 'Get USDC';
43-
}
4438
return text;
45-
}, [lifecycleStatus?.statusName, lifecycleStatus?.statusData, text]);
39+
}, [lifecycleStatus?.statusName, text]);
4640
const shouldRenderIcon = buttonText === text && iconSvg;
4741

4842
return (

src/checkout/components/CheckoutProvider.test.tsx

+9-25
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { useAccount, useConnect, useSwitchChain } from 'wagmi';
1212
import { useWaitForTransactionReceipt } from 'wagmi';
1313
import { useCallsStatus } from 'wagmi/experimental';
1414
import { useWriteContracts } from 'wagmi/experimental';
15+
import { openPopup } from '../../internal/utils/openPopup';
1516
import { useOnchainKit } from '../../useOnchainKit';
1617
import { useIsWalletACoinbaseSmartWallet } from '../../wallet/hooks/useIsWalletACoinbaseSmartWallet';
1718
import { GENERIC_ERROR_MESSAGE } from '../constants';
@@ -42,6 +43,10 @@ vi.mock('../../useOnchainKit', () => ({
4243
useOnchainKit: vi.fn(),
4344
}));
4445

46+
vi.mock('../../internal/utils/openPopup', () => ({
47+
openPopup: vi.fn(),
48+
}));
49+
4550
const windowOpenMock = vi.fn();
4651

4752
const TestComponent = () => {
@@ -197,23 +202,6 @@ describe('CheckoutProvider', () => {
197202
});
198203
});
199204

200-
it('should handle insufficient balance', async () => {
201-
(useCommerceContracts as Mock).mockReturnValue(() =>
202-
Promise.resolve({ insufficientBalance: true, priceInUSDC: '10' }),
203-
);
204-
render(
205-
<CheckoutProvider>
206-
<TestComponent />
207-
</CheckoutProvider>,
208-
);
209-
fireEvent.click(screen.getByText('Submit'));
210-
await waitFor(() => {
211-
expect(screen.getByTestId('error-message').textContent).toBe(
212-
'You need at least 10 USDC to continue with payment',
213-
);
214-
});
215-
});
216-
217205
it('should handle successful transaction', async () => {
218206
(useWaitForTransactionReceipt as Mock).mockReturnValue({
219207
data: { status: 'success' },
@@ -431,16 +419,12 @@ describe('CheckoutProvider', () => {
431419
);
432420
fireEvent.click(screen.getByText('Submit'));
433421
await waitFor(() => {
434-
expect(screen.getByTestId('error-message').textContent).toBe(
435-
'You need at least 10 USDC to continue with payment',
422+
expect(openPopup as Mock).toHaveBeenCalledWith(
423+
expect.objectContaining({
424+
url: 'https://keys.coinbase.com/fund?asset=USDC&chainId=8453&presetCryptoAmount=10',
425+
}),
436426
);
437427
});
438-
fireEvent.click(screen.getByText('Submit'));
439-
expect(windowOpenMock).toHaveBeenCalledWith(
440-
'https://keys.coinbase.com/fund?asset=USDC&chainId=8453&presetCryptoAmount=10',
441-
'_blank',
442-
'noopener,noreferrer',
443-
);
444428
});
445429

446430
it('should handle errors when fetching contracts', async () => {

src/checkout/components/CheckoutProvider.tsx

+13-39
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { coinbaseWallet } from 'wagmi/connectors';
1414
import { useWriteContracts } from 'wagmi/experimental';
1515
import { useCallsStatus } from 'wagmi/experimental';
1616
import { useValue } from '../../internal/hooks/useValue';
17+
import { getWindowDimensions } from '../../internal/utils/getWindowDimensions';
18+
import { openPopup } from '../../internal/utils/openPopup';
1719
import { isUserRejectedRequestError } from '../../transaction/utils/isUserRejectedRequestError';
1820
import { useOnchainKit } from '../../useOnchainKit';
1921
import { useIsWalletACoinbaseSmartWallet } from '../../wallet/hooks/useIsWalletACoinbaseSmartWallet';
@@ -23,12 +25,7 @@ import {
2325
NO_CONTRACTS_ERROR,
2426
USER_REJECTED_ERROR,
2527
} from '../constants';
26-
import {
27-
CHECKOUT_INSUFFICIENT_BALANCE_ERROR,
28-
CHECKOUT_INSUFFICIENT_BALANCE_ERROR_MESSAGE,
29-
CHECKOUT_LIFECYCLESTATUS,
30-
CheckoutErrorCode,
31-
} from '../constants';
28+
import { CHECKOUT_LIFECYCLESTATUS, CheckoutErrorCode } from '../constants';
3229
import { useCommerceContracts } from '../hooks/useCommerceContracts';
3330
import { useLifecycleStatus } from '../hooks/useLifecycleStatus';
3431
import type { CheckoutContextType, CheckoutProviderReact } from '../types';
@@ -212,26 +209,6 @@ export function CheckoutProvider({
212209
);
213210
return;
214211
}
215-
// Open funding flow
216-
// TODO: Deprecate this once we have USDC Magic Spend
217-
if (
218-
lifecycleStatus.statusName === CHECKOUT_LIFECYCLESTATUS.ERROR &&
219-
lifecycleStatus.statusData?.code ===
220-
CheckoutErrorCode.INSUFFICIENT_BALANCE
221-
) {
222-
window.open(
223-
`https://keys.coinbase.com/fund?asset=USDC&chainId=8453&presetCryptoAmount=${priceInUSDCRef.current}`,
224-
'_blank',
225-
'noopener,noreferrer',
226-
);
227-
// Reset status
228-
setErrorMessage('');
229-
updateLifecycleStatus({
230-
statusName: CHECKOUT_LIFECYCLESTATUS.INIT,
231-
statusData: {},
232-
});
233-
return;
234-
}
235212
if (errorMessage === USER_REJECTED_ERROR) {
236213
// Reset status if previous request was a rejection
237214
setErrorMessage('');
@@ -285,19 +262,17 @@ export function CheckoutProvider({
285262

286263
// Check for sufficient balance
287264
if (insufficientBalanceRef.current && priceInUSDCRef.current) {
288-
setErrorMessage(
289-
CHECKOUT_INSUFFICIENT_BALANCE_ERROR_MESSAGE(priceInUSDCRef.current),
290-
);
291-
updateLifecycleStatus({
292-
statusName: CHECKOUT_LIFECYCLESTATUS.ERROR,
293-
statusData: {
294-
code: CheckoutErrorCode.INSUFFICIENT_BALANCE,
295-
error: CHECKOUT_INSUFFICIENT_BALANCE_ERROR,
296-
message: CHECKOUT_INSUFFICIENT_BALANCE_ERROR_MESSAGE(
297-
priceInUSDCRef.current,
298-
),
299-
},
265+
const { height, width } = getWindowDimensions('md');
266+
openPopup({
267+
url: `https://keys.coinbase.com/fund?asset=USDC&chainId=8453&presetCryptoAmount=${priceInUSDCRef.current}`,
268+
target: '_blank',
269+
height,
270+
width,
300271
});
272+
// Reset state
273+
insufficientBalanceRef.current = false;
274+
priceInUSDCRef.current = undefined;
275+
fetchedDataUseEffect.current = false;
301276
return;
302277
}
303278

@@ -362,7 +337,6 @@ export function CheckoutProvider({
362337
isConnected,
363338
isSmartWallet,
364339
isSponsored,
365-
lifecycleStatus.statusData,
366340
lifecycleStatus.statusName,
367341
paymaster,
368342
switchChainAsync,

src/checkout/constants.ts

-5
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ export const CHECKOUT_TOO_MANY_REQUESTS_ERROR_MESSAGE =
88

99
export const CHECKOUT_INSUFFICIENT_BALANCE_ERROR =
1010
'User has insufficient balance';
11-
export const CHECKOUT_INSUFFICIENT_BALANCE_ERROR_MESSAGE = (
12-
priceInUSD: string,
13-
) => {
14-
return `You need at least ${priceInUSD} USDC to continue with payment`;
15-
};
1611
export const CHECKOUT_INVALID_CHARGE_ERROR_MESSAGE =
1712
'CHECKOUT_INVALID_CHARGE_ERROR';
1813
export const CHECKOUT_INVALID_PARAMETER_ERROR_MESSAGE =

0 commit comments

Comments
 (0)