-
Notifications
You must be signed in to change notification settings - Fork 152
feat: add with-abacate-pay example #80
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: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
|
|
||
| # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
|
||
| # dependencies | ||
| /node_modules | ||
| /.pnp | ||
| .pnp.js | ||
|
|
||
| # testing | ||
| /coverage | ||
|
|
||
| # misc | ||
| .DS_Store | ||
| *.pem | ||
|
|
||
| # debug | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
| .pnpm-debug.log* | ||
|
|
||
| # local env files | ||
| .env*.local | ||
|
|
||
| out/ | ||
| build/ | ||
| dist/ | ||
|
|
||
| # plasmo | ||
| .plasmo | ||
|
|
||
| # typescript | ||
| .tsbuildinfo |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /** | ||
| * @type {import('prettier').Options} | ||
| */ | ||
| export default { | ||
| printWidth: 80, | ||
| tabWidth: 2, | ||
| useTabs: false, | ||
| semi: false, | ||
| singleQuote: false, | ||
| trailingComma: "none", | ||
| bracketSpacing: true, | ||
| bracketSameLine: true, | ||
| plugins: ["@ianvs/prettier-plugin-sort-imports"], | ||
| importOrder: [ | ||
| "<BUILTIN_MODULES>", // Node.js built-in modules | ||
| "<THIRD_PARTY_MODULES>", // Imports not matched by other special words or groups. | ||
| "", // Empty line | ||
| "^@plasmo/(.*)$", | ||
| "", | ||
| "^@plasmohq/(.*)$", | ||
| "", | ||
| "^~(.*)$", | ||
| "", | ||
| "^[./]" | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| This is a [Plasmo extension](https://docs.plasmo.com/) project bootstrapped with [`plasmo init`](https://www.npmjs.com/package/plasmo). | ||
|
|
||
| ## Getting Started | ||
|
|
||
| First, run the development server: | ||
|
|
||
| ```bash | ||
| pnpm dev | ||
| # or | ||
| npm run dev | ||
| ``` | ||
|
|
||
| Open your browser and load the appropriate development build. For example, if you are developing for the chrome browser, using manifest v3, use: `build/chrome-mv3-dev`. | ||
|
|
||
| You can start editing the popup by modifying `popup.tsx`. It should auto-update as you make changes. To add an options page, simply add a `options.tsx` file to the root of the project, with a react component default exported. Likewise to add a content page, add a `content.ts` file to the root of the project, importing some module and do some logic, then reload the extension on your browser. | ||
|
|
||
| For further guidance, [visit our Documentation](https://docs.plasmo.com/) | ||
|
|
||
| ## Making production build | ||
|
|
||
| Run the following: | ||
|
|
||
| ```bash | ||
| pnpm build | ||
| # or | ||
| npm run build | ||
| ``` | ||
|
|
||
| This should create a production bundle for your extension, ready to be zipped and published to the stores. | ||
|
|
||
| ## Submit to the webstores | ||
|
|
||
| The easiest way to deploy your Plasmo extension is to use the built-in [bpp](https://bpp.browser.market) GitHub action. Prior to using this action however, make sure to build your extension and upload the first version to the store to establish the basic credentials. Then, simply follow [this setup instruction](https://docs.plasmo.com/framework/workflows/submit) and you should be on your way for automated submission! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| { | ||
| "name": "with-abacate-pay", | ||
| "displayName": "With abacate pay", | ||
| "version": "0.0.1", | ||
| "description": "A plasmo extension example using abacate pay, a brazilian payment gateway for PIX ", | ||
| "author": "VINNE7", | ||
| "scripts": { | ||
| "dev": "plasmo dev", | ||
| "build": "plasmo build", | ||
| "package": "plasmo package" | ||
| }, | ||
| "dependencies": { | ||
| "@types/cors": "^2.8.18", | ||
| "cors": "^2.8.5", | ||
| "plasmo": "0.90.3", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should point to workspace dep |
||
| "react": "18.2.0", | ||
| "react-dom": "18.2.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@ianvs/prettier-plugin-sort-imports": "4.1.1", | ||
| "@types/chrome": "0.0.258", | ||
| "@types/node": "20.11.5", | ||
| "@types/react": "18.2.48", | ||
| "@types/react-dom": "18.2.18", | ||
| "prettier": "3.2.4", | ||
| "typescript": "5.3.3" | ||
| }, | ||
| "manifest": { | ||
| "host_permissions": [ | ||
| "$PLASMO_PUBLIC_BACKEND_URL/*", | ||
| "https://*/*" | ||
| ], | ||
| "permissions": [ | ||
| "activeTab" | ||
| ] | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| import { useRef, useState } from "react" | ||
|
|
||
| const BACKEND_URL = process.env.PLASMO_PUBLIC_BACKEND_URL | ||
|
|
||
| const initialForm = { | ||
| name: "", | ||
| email: "", | ||
| cellphone: "", | ||
| taxId: "", | ||
| amount: 1, | ||
| description: "PIX payment via AbacatePay" | ||
| } | ||
|
|
||
| function IndexPopup() { | ||
| const [form, setForm] = useState(initialForm) | ||
| const [loading, setLoading] = useState(false) | ||
| const [error, setError] = useState("") | ||
| const [payment, setPayment] = useState<any>(null) | ||
| const [status, setStatus] = useState<string>("") | ||
| const pollingRef = useRef<NodeJS.Timeout | null>(null) | ||
|
|
||
| const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| const { name, value } = e.target | ||
| setForm((f) => ({ ...f, [name]: value })) | ||
| } | ||
|
|
||
| const handleNumber = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| setForm((f) => ({ ...f, amount: Number(e.target.value) })) | ||
| } | ||
|
|
||
| const handleSubmit = async (e: React.FormEvent) => { | ||
| e.preventDefault() | ||
| setLoading(true) | ||
| setError("") | ||
| setPayment(null) | ||
| setStatus("") | ||
| try { | ||
| const res = await fetch(`${BACKEND_URL}/api/create-payment`, { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems generic enough that I feel like this should be a dedicated docs page rather than an example :-?... Maybe we stage a poll to see who might be most interested for this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be honest i don't think anybody will vote on it, I don't see many brazilian users in plasmo and browser extension development isn't mainstream here. I've just wanted to mess with this gateway and make this example so in case one day extension development becomes more mainstream we already would have a brazilian api gateway example. But yeah, abacatepay by nature is a very simple tool so it should be as generic as this. |
||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ | ||
| amount: form.amount, | ||
| expiresIn: 300, | ||
| description: form.description, | ||
| customer: { | ||
| name: form.name, | ||
| cellphone: form.cellphone, | ||
| email: form.email, | ||
| taxId: form.taxId | ||
| } | ||
| }) | ||
| }) | ||
| if (!res.ok) { | ||
| console.log(res) | ||
| throw new Error("Error creating payment") | ||
| } | ||
| const data = await res.json() | ||
| setPayment(data.data) | ||
| setStatus(data.data.status) | ||
| startPolling(data.data.id) | ||
| } catch (err: any) { | ||
| setError(err.message) | ||
| } finally { | ||
| setLoading(false) | ||
| } | ||
| } | ||
|
|
||
| const startPolling = (id: string) => { | ||
| if (pollingRef.current) clearInterval(pollingRef.current) | ||
| pollingRef.current = setInterval(async () => { | ||
| try { | ||
| const res = await fetch(`${BACKEND_URL}/api/check-status/${id}`) | ||
| const data = await res.json() | ||
| setStatus(data.status || data.data?.status) | ||
| if (data.status === "paid" || data.status === "expired") { | ||
| if (pollingRef.current) clearInterval(pollingRef.current) | ||
| } | ||
| } catch (err) { | ||
| // ignore polling errors | ||
| } | ||
| }, 3000) | ||
| } | ||
|
|
||
| const handleSimulate = async () => { | ||
| if (!payment?.id) return | ||
| setLoading(true) | ||
| setError("") | ||
| try { | ||
| const res = await fetch(`${BACKEND_URL}/api/simulate-payment`, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ id: payment.id }) | ||
| }) | ||
| if (!res.ok) throw new Error("Error at simulate payment") | ||
| } catch (err: any) { | ||
| setError(err.message) | ||
| } finally { | ||
| setLoading(false) | ||
| } | ||
| } | ||
|
|
||
| return ( | ||
| <div style={{ padding: 16, minWidth: 320, fontFamily: "sans-serif" }}> | ||
| <h2>AbacatePay PIX Example</h2> | ||
| <form | ||
| onSubmit={handleSubmit} | ||
| style={{ display: "flex", flexDirection: "column", gap: 8 }}> | ||
| <input | ||
| name="name" | ||
| placeholder="Name" | ||
| value={form.name} | ||
| onChange={handleChange} | ||
| required | ||
| /> | ||
| <input | ||
| name="email" | ||
| placeholder="Email" | ||
| value={form.email} | ||
| onChange={handleChange} | ||
| required | ||
| /> | ||
| <input | ||
| name="cellphone" | ||
| placeholder="Cellphone" | ||
| value={form.cellphone} | ||
| onChange={handleChange} | ||
| required | ||
| /> | ||
| <input | ||
| name="taxId" | ||
| placeholder="CPF (Brazilian Tax ID)" | ||
| value={form.taxId} | ||
| onChange={handleChange} | ||
| required | ||
| /> | ||
| <input | ||
| name="amount" | ||
| type="number" | ||
| min={1} | ||
| placeholder="Amount (BRL)" | ||
| value={form.amount} | ||
| onChange={handleNumber} | ||
| required | ||
| /> | ||
| <input | ||
| name="description" | ||
| placeholder="Description" | ||
| value={form.description} | ||
| onChange={handleChange} | ||
| required | ||
| /> | ||
| <button type="submit" disabled={loading} style={{ marginTop: 8 }}> | ||
| {loading ? "Processing..." : "Generate PIX QR Code"} | ||
| </button> | ||
| </form> | ||
| {error && <div style={{ color: "red", marginTop: 8 }}>{error}</div>} | ||
| {payment && ( | ||
| <div style={{ marginTop: 16 }}> | ||
| <div> | ||
| Status: <b>{status || payment.status || "PENDING"}</b> | ||
| </div> | ||
| {payment.brCode && ( | ||
| <div style={{ margin: "16px 0" }}> | ||
| <img | ||
| src={`https://api.qrserver.com/v1/create-qr-code/?data=${encodeURIComponent(payment.brCode)}&size=200x200`} | ||
| alt="PIX QR Code" | ||
| /> | ||
| <div | ||
| style={{ wordBreak: "break-all", fontSize: 12, marginTop: 8 }}> | ||
| {payment.brCode} | ||
| </div> | ||
| </div> | ||
| )} | ||
| {status !== "paid" && status !== "expired" && ( | ||
| <button | ||
| onClick={handleSimulate} | ||
| disabled={loading} | ||
| style={{ marginTop: 8 }}> | ||
| Simulate Payment | ||
| </button> | ||
| )} | ||
| </div> | ||
| )} | ||
| <div style={{ marginTop: 24, fontSize: 12, color: "#888" }}> | ||
| <a | ||
| href="https://docs.abacatepay.com/pages/introduction" | ||
| target="_blank" | ||
| rel="noreferrer"> | ||
| AbacatePay Docs | ||
| </a> | ||
| </div> | ||
| </div> | ||
| ) | ||
| } | ||
|
|
||
| export default IndexPopup | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| { | ||
| "extends": "plasmo/templates/tsconfig.base", | ||
| "exclude": [ | ||
| "node_modules" | ||
| ], | ||
| "include": [ | ||
| ".plasmo/index.d.ts", | ||
| "./**/*.ts", | ||
| "./**/*.tsx" | ||
| ], | ||
| "compilerOptions": { | ||
| "paths": { | ||
| "~*": [ | ||
| "./*" | ||
| ] | ||
| }, | ||
| "baseUrl": "." | ||
| } | ||
| } |
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.
Should be fixed dependency