Skip to content
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

paykit: create payments directly from the button #15

Merged
merged 6 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/nextjs-app/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

NEXT_PUBLIC_DAIMOPAY_API_URL="http://localhost:4000"
49 changes: 0 additions & 49 deletions examples/nextjs-app/app/page.tsx

This file was deleted.

5 changes: 4 additions & 1 deletion examples/nextjs-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
"lint": "next lint"
},
"dependencies": {
"@tanstack/react-query": "^5.51.11",
"@daimo/pay": "*",
"@tanstack/react-query": "^5.51.11",
"autoprefixer": "^10.4.20",
"next": "14.2.13",
"postcss": "^8.4.49",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "^3.4.17",
"viem": "^2.21.10",
"wagmi": "^2.12.0"
},
Expand Down
6 changes: 6 additions & 0 deletions examples/nextjs-app/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
36 changes: 36 additions & 0 deletions examples/nextjs-app/src/app/DemoBasic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";

import { baseUSDC } from "@daimo/contract";
import { DaimoPayButton } from "@daimo/pay";
import { getAddress } from "viem";
import { Text, TextLink } from "../shared/tailwind-catalyst/text";
import { APP_ID, Container, printEvent } from "./shared";

export function DemoBasic() {
return (
<Container>
<Text>
This shows a basic payment from any coin on any chain. The recipient
receives USDC on Base.
</Text>
<div />
<DaimoPayButton
appId={APP_ID}
toChain={8453}
toAddress="0xFBfa6A0D1F44b60d7CCA4b95d5a2CfB15246DB0D"
toUnits="0.12" /* $0.12 USDC */
toToken={getAddress(baseUSDC.token)}
onPaymentStarted={printEvent}
onPaymentCompleted={printEvent}
/>
<Text>
<TextLink
href="https://github.com/daimo-eth/paykit/blob/main/examples/nextjs-app/app/DemoBasic.tsx"
target="_blank"
>
View on Github ↗
</TextLink>
</Text>
</Container>
);
}
70 changes: 70 additions & 0 deletions examples/nextjs-app/src/app/DemoCheckout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"use client";

import { getAddressContraction, PaymentStartedEvent } from "@daimo/common";
import { baseUSDC } from "@daimo/contract";
import { DaimoPayButton } from "@daimo/pay";
import { useCallback, useState } from "react";
import { getAddress } from "viem";
import { Code, Text, TextLink } from "../shared/tailwind-catalyst/text";
import { APP_ID, Columns, Container, printEvent } from "./shared";

export function DemoCheckout() {
const [payId, setPayId] = useState<string>();

const start = useCallback((e: PaymentStartedEvent) => {
printEvent(e);
const payId = e.paymentId;
setPayId(payId);
// Save payId to your backend here. This ensures that you'll be able to
// correlate all incoming payments even if the user loses network, etc.
// await saveCartCheckout(payId, ...);
}, []);

return (
<Container>
<Text>
For robust checkout, save the payId in <Code>onPaymentStarted</Code>.
This ensures you&apos;ll be able to correlate incoming payments with a
cart (or a user ID, form submission, etc) even if the user closes the
tab.
</Text>
<Text>
In addition to callbacks like <Code>onPaymentSucceeded</Code>, Daimo Pay
supports{" "}
<TextLink href="https://paydocs.daimo.com/webhooks">webhooks</TextLink>{" "}
to track payment status reliably on the backend.
</Text>
<div />
<Columns>
<div className="flex-1">
<DaimoPayButton
appId={APP_ID}
toChain={8453}
toAddress="0xFBfa6A0D1F44b60d7CCA4b95d5a2CfB15246DB0D"
toUnits="0.42" /* $0.42 USDC */
toToken={getAddress(baseUSDC.token)}
intent="Purchase"
preferredChains={[
8453,
]} /* Rank specific chain + coin first in UI. */
preferredTokens={[
{ chain: 8453, address: getAddress(baseUSDC.token) },
]}
onPaymentStarted={start}
/>
</div>
<div className="flex-1">
<Text>PayID {payId ? getAddressContraction(payId) : "TBD"}</Text>
</div>
</Columns>
<Text>
<TextLink
href="https://github.com/daimo-eth/paykit/blob/main/examples/nextjs-app/app/DemoCheckout.tsx"
target="_blank"
>
View on Github ↗
</TextLink>
</Text>
</Container>
);
}
115 changes: 115 additions & 0 deletions examples/nextjs-app/src/app/DemoContract.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"use client";

import { PaymentCompletedEvent, PaymentStartedEvent } from "@daimo/common";
import { arbitrum, getChainExplorerByChainId } from "@daimo/contract";
import { DaimoPayButton } from "@daimo/pay";
import { useState } from "react";
import { encodeFunctionData, parseAbi, zeroAddress } from "viem";
import { useReadContract } from "wagmi";
import { Text, TextLink } from "../shared/tailwind-catalyst/text";
import { APP_ID, Columns, Container, printEvent } from "./shared";

export function DemoContract() {
const counterAddr = "0x7f3c168DD11379748EeF71Bea70371eBA3327Ca5";
const counterAbi = parseAbi([
"function increment(string) external payable",
"function counter() external view returns (uint256)",
]);

// Load count from chain
const {
data: count,
error,
refetch,
} = useReadContract({
chainId: arbitrum.chainId,
abi: counterAbi,
address: counterAddr,
functionName: "counter",
});

// Show transaction, once we're done
const [successUrl, setSuccessUrl] = useState<string>();

// Reload on successful action
const onStart = (e: PaymentStartedEvent) => {
printEvent(e);
setSuccessUrl(undefined);
};
const onSuccess = (e: PaymentCompletedEvent) => {
printEvent(e);
setSuccessUrl(`${getChainExplorerByChainId(e.chainId)}/tx/${e.txHash}`);
refetch();
};

return (
<Container>
<Text>
Daimo Pay supports arbitrary contract calls. This lets you offer
one-click checkout for digital goods. It also enables optimal
onboarding.
</Text>
<Text>
For example, imagine a new user who wants to use a prediction market.
You can let them place a prediction immediately as part of their
onboarding, paying from any coin on any chain.
</Text>
<Text>
Demo: pay 0.0001 ETH to increment{" "}
<TextLink
href={`https://arbiscan.io/address/${counterAddr}#code`}
target="_blank"
>
this counter
</TextLink>
.
</Text>
<div />
<Columns>
<div className="flex-1">
<DaimoPayButton
appId={APP_ID}
toChain={arbitrum.chainId}
toAddress={counterAddr}
toToken={zeroAddress} /* Zero address = ETH */
toUnits="0.0001" /* 0.0001 ETH */
toCallData={encodeFunctionData({
abi: counterAbi,
functionName: "increment",
args: ["0x"],
})}
intent="Increment"
onPaymentStarted={onStart}
onPaymentCompleted={onSuccess}
onPaymentBounced={printEvent}
/>
</div>
<div className="flex-1">
<Text>Count {count != null && Number(count)}</Text>
</div>
</Columns>
<Columns>
<div className="flex-1">
<Text>
<TextLink
href="https://github.com/daimo-eth/paykit/blob/main/examples/nextjs-app/app/DemoContract.tsx"
target="_blank"
>
View on Github ↗
</TextLink>
</Text>
</div>
<div className="flex-1">
{error && <Text>{error.message}</Text>}
{successUrl && (
<Text>
<TextLink href={successUrl} target="_blank">
View transaction
</TextLink>
</Text>
)}
</div>
</Columns>
</Container>
);
}
39 changes: 39 additions & 0 deletions examples/nextjs-app/src/app/DemoDeposit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use client";

import { baseUSDC } from "@daimo/contract";
import { DaimoPayButton } from "@daimo/pay";
import { getAddress } from "viem";
import { Code, Text, TextLink } from "../shared/tailwind-catalyst/text";
import { APP_ID, Container, printEvent } from "./shared";

export function DemoDeposit() {
return (
<Container>
<Text>
Onboard users in one click. For any-chain deposits, set an initial
amount and use <Code>amountEditable</Code> to let the user choose.
</Text>
<div />
<DaimoPayButton
appId={APP_ID}
toChain={8453}
toAddress="0xFBfa6A0D1F44b60d7CCA4b95d5a2CfB15246DB0D"
toUnits="10.00" /* $10.00 USDC */
toToken={getAddress(baseUSDC.token)}
intent="Deposit"
amountEditable
preferredChains={[10]} /* Show assets on Optimism first. */
onPaymentStarted={printEvent}
onPaymentCompleted={printEvent}
/>
<Text>
<TextLink
href="https://github.com/daimo-eth/paykit/blob/main/examples/nextjs-app/app/DemoDeposit.tsx"
target="_blank"
>
View on Github ↗
</TextLink>
</Text>
</Container>
);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { Metadata } from 'next';
import { type ReactNode } from 'react';
import '../styles/globals.css';
import type { Metadata } from "next";
import { type ReactNode } from "react";
import { Providers } from "./providers";

import { Providers } from './providers';
import "../styles/tailwind.css";

export const metadata: Metadata = {
title: 'ConnectKit Next.js Example',
description: 'By Family',
title: "ConnectKit Next.js Example",
description: "By Family",
};

export default function RootLayout(props: { children: ReactNode }) {
Expand Down
Loading