pinme-uniwebpay
Use when generating, modifying, or reviewing PinMe Worker (Cloudflare Worker TypeScript) code that accepts payments through UniwebPay — payment links, products/prices, checkout sessions, payment status reads, refunds, subscriptions, or handling UniwebPay webhooks with @uniwebpay/sdk in a PinMe proje…
Install / Use
npx skills add glitternetwork/pinme --skill pinme-uniwebpayInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Content & MediaSupported Platforms
Our assessment of pinme-uniwebpay
pinme-uniwebpay scores 95/100 on our quality scale, 85th of 735 Content & Media skills we index (top 12%).
Its SKILL.md is 28 KB long, well organised into 14 sections with 14 code examples: a thorough specification that gives an agent plenty to work with.
With 3,745 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated 16 days ago, so pinme-uniwebpay is actively maintained.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
pinme-uniwebpay compared with similar skills
All 4 of these similar skills score higher than pinme-uniwebpay; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| pinme-uniwebpay (this skill)by glitternetwork | 95 | 3.7k | 16d ago | SKILL.md |
| headroomby headroomlabs-ai | 100 | 74.0k | 1d ago | CLAUDE.md |
| rufloby ruvnet | 100 | 73.4k | today | CLAUDE.md |
| siyuanby siyuan-note | 100 | 46.5k | today | MCP Server |
| algorithmic-artby anthropics | 100 | 177.9k | 5d ago | SKILL.md |
Frequently asked questions
- How do I install pinme-uniwebpay?
- Run
npx skills add glitternetwork/pinme --skill pinme-uniwebpay. The install tabs above show the steps for each supported agent. - Which AI agents does pinme-uniwebpay work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is pinme-uniwebpay safe to use?
- It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is pinme-uniwebpay still maintained?
- The repository was last updated 16 days ago, so pinme-uniwebpay is actively maintained.
Skill content
View source on GitHubname: pinme-uniwebpay description: Use when generating, modifying, or reviewing PinMe Worker (Cloudflare Worker TypeScript) code that accepts payments through UniwebPay — payment links, products/prices, checkout sessions, payment status reads, refunds, subscriptions, or handling UniwebPay webhooks with @uniwebpay/sdk in a PinMe project.
PinMe UniwebPay Payment Integration
Guides writing payment services in a PinMe Worker (Cloudflare Worker TypeScript) that call UniwebPay directly through @uniwebpay/sdk.
Core model: PinMe provisions the UniwebPay wallet and keys per PinMe user (not per project) and injects UNIWEB_* environment bindings at Worker deploy time; Worker code calls UniwebPay directly with the SDK — it does not go through PinMe payment proxy routes, and it must not call the legacy VibeCash APIs.
Environment Binding Contract
export interface Env {
UNIWEB_SECRET: string; // PinMe-provisioned sk_server_ key (server-side only)
UNIWEB_WEBHOOK_SECRET?: string; // wallet-level whsec_, used to verify webhook signatures
UNIWEB_API_URL?: string; // UniwebPay API endpoint override (default https://apiskill.uniwebpay.com)
UNIWEB_PAY_URL?: string; // UniwebPay checkout host override (default https://skill.uniwebpay.com)
UNIWEB_WALLET_ID?: string; // user-level wallet id (wal_), diagnostics/reconciliation only
WORKER_URL?: string; // this project's public URL: https://{projectName}.{platform api domain}
PROJECT_NAME?: string; // PinMe project name
DB?: D1Database; // project D1 (if enabled)
}
Injection rules (metadata is rebuilt server-side by PinMe at deploy time; client-supplied bindings are ignored):
- The
UNIWEB_*bindings are injected only after the user's UniwebPay credentials have been provisioned. Newly created projects are provisioned automatically and get them immediately; existing projects must be redeployed after enabling UniwebPay or rotating keys to pick up new bindings. WORKER_URL,PROJECT_NAME,API_KEY,DBand other base bindings are injected on every deploy, independent of UniwebPay.- All projects owned by the same PinMe user share one wallet, one
sk_server_, and onewhsec_. - PinMe never gives the full wallet secret (
sk_live_) to a Worker. Do not ask the user for it, and do not put it in code,wrangler.toml,.dev.vars, responses, logs, D1, or frontend bundles. - If
UNIWEB_SECRETis missing at runtime, the user has not enabled UniwebPay or has not redeployed — tell the user to enable it and redeploy; never fabricate a value.
SDK Client
Always instantiate on the server side (the Worker); the SDK throws when run in a browser:
import Uniweb from "@uniwebpay/sdk";
function uniwebClient(env: Env): Uniweb {
return new Uniweb(env.UNIWEB_SECRET, {
baseUrl: env.UNIWEB_API_URL,
payUrl: env.UNIWEB_PAY_URL,
});
}
- The constructor's first positional argument is the key (must have an
sk_server_orsk_live_prefix); the second is optional options:{ baseUrl?, payUrl?, timeout? (default 30s), maxRetries? (default 2) }. - The SDK auto-retries only GET/DELETE on 429/5xx; POST/PATCH are never retried (avoids duplicate charges).
- Install
@uniwebpay/sdkonly when Worker code imports it; pick the package manager from the project's existing lockfile.
Choosing an Integration Path
| Scenario | Approach | Returns |
|------|------|------|
| Fixed-amount one-time collection | uniweb.links.create(...) | Permanent, reusable /p/ link (one-time payments only) |
| Stable product catalog | products.create + prices.create once, store the priceId | Price carries a permanent paymentUrl (/buy/ link) |
| Dynamic cart/order | Reuse or create a price, then uniweb.checkout.create(...) | session.url — one-time, expires in 24 hours |
| Subscriptions | Recurring price + checkout.create({ mode: "subscription" }) or subscriptions.create | Same as above |
| Server-side payment status checks | payments.get / list | Server routes only |
Amounts are always integer minor units (cents). Default currency convention is SGD unless the app has a stronger existing convention. Do not create a new product/price on every page view — create stable catalog items once and persist the priceId.
Payment Methods and Currency Rules
| Method | Supported currencies |
|------|---------|
| card | SGD, USD, EUR, GBP, JPY, CNY, HKD, AUD, MYR, THB (minimum 10 minor units) |
| wechat | SGD only |
| alipay | SGD only |
| paynow | SGD only |
- The QR methods (wechat/alipay/paynow) all support SGD only — never generate "CNY via WeChat/Alipay" code.
- Subscriptions (recurring /
mode: "subscription") usecardonly. - When
paymentMethodTypesis omitted, the server picks sensible defaults for the currency; when passed explicitly, validate user input against the table above first.
SDK Surface Quick Reference
The surface below is verified against source. All parameter fields are camelCase (priceId, webhookUrl, startingAfter, …); the SDK handles wire-level conversion itself. list() returns { data: T[], hasMore: boolean }; listAll() is an async generator available on products, prices, payments, customers, subscriptions, and links (not on checkout or refunds).
Products (webhookUrl is the per-product callback override):
await uniweb.products.create({ name, description?, webhookUrl?, metadata? });
await uniweb.products.list({ limit?, startingAfter? });
await uniweb.products.get(productId);
await uniweb.products.update(productId, { name?, description?, webhookUrl?, active?, metadata? });
await uniweb.products.del(productId);
for await (const product of uniweb.products.listAll()) {}
Prices (the returned price carries a permanent paymentUrl; deactivate takes it off sale):
await uniweb.prices.create({
productId,
amount, // integer minor units
currency, // e.g. "SGD"
type, // "one_time" | "recurring"
interval?, // "day" | "week" | "month" | "year"; recurring only
intervalCount?,
trialPeriodDays?,
metadata?,
});
await uniweb.prices.list({ productId?, limit?, startingAfter? });
await uniweb.prices.get(priceId);
await uniweb.prices.update(priceId, { active });
await uniweb.prices.activate(priceId);
await uniweb.prices.deactivate(priceId);
for await (const price of uniweb.prices.listAll({ productId? })) {}
Checkout sessions (do not accept webhookUrl — events resolve through the price → product → wallet chain; the URL is one-time and expires after 24 hours):
await uniweb.checkout.create({
mode, // "payment" | "subscription"
lineItems: [{ priceId, quantity }],
successUrl?,
cancelUrl?,
customerEmail?,
customerId?,
trialPeriodDays?,
paymentMethodTypes?, // ["card", "wechat", "alipay", "paynow"]
metadata?,
});
await uniweb.checkout.list({ limit?, startingAfter? });
await uniweb.checkout.get(checkoutSessionId);
Payments (for server-side status checks; only mark a local order paid when amount, currency, metadata, and order state all match expectations):
await uniweb.payments.create({ amount, currency, customerId?, metadata? });
await uniweb.payments.list({ status?, customerId?, limit?, startingAfter? });
await uniweb.payments.get(paymentId, { gateway? });
await uniweb.payments.listRefunds(paymentId);
await uniweb.payments.sync(paymentId);
await uniweb.payments.void(paymentId);
for await (const payment of uniweb.payments.listAll({ status?, customerId? })) {}
Refunds (no list/listAll — use payments.listRefunds):
await uniweb.refunds.create({ paymentId, amount?, reason?, offlineRefundFlag? });
await uniweb.refunds.get(refundId, { gateway? });
Customers:
await uniweb.customers.create({ email, name?, metadata? });
await uniweb.customers.list({ email?, limit?, startingAfter? });
await uniweb.customers.get(customerId);
await uniweb.customers.update(customerId, { email?, name?, metadata? });
await uniweb.customers.del(customerId);
for await (const customer of uniweb.customers.listAll({ email? })) {}
Subscriptions (states include trialing / active / past_due / unpaid / canceled; update access only from verified webhooks or a trusted server-side reconciliation job):
await uniweb.subscriptions.create({ customerId, priceId, paymentMethodId?, trialPeriodDays?, metadata? });
await uniweb.subscriptions.list({ customerId?, status?, limit?, startingAfter? });
await uniweb.subscriptions.get(subscriptionId);
await uniweb.subscriptions.update(subscriptionId, { cancelAtPeriodEnd? });
await uniweb.subscriptions.cancel(subscriptionId); // cancel immediately
await uniweb.subscriptions.resume(subscriptionId); // undo cancelAtPeriodEnd
for await (const subscription of uniweb.subscriptions.listAll({ customerId?, status? })) {}
Payment links (permanent reusable /p/ links, one-time collection only; webhookUrl is the per-link callback override):
await uniweb.links.create({
amount,
currency,
name?,
description?,
successUrl?,
cancelUrl?,
webhookUrl?,
paymentMethodTypes?,
metadata?,
});
await uniweb.links.list({ limit?, startingAfter? });
await uniweb.links.get(paymentLinkId);
await uniweb.links.update(paymentLinkId, { name?, description?, successUrl?, cancelUrl?, webhookUrl?, active? });
await uniweb.links.deactivate(paymentLinkId);
for await (const link of uniweb.links.listAll()) {}
Wallet and wallet-level webhook configuration (danger zone: affects the wallet shared by ALL of the user's projects):
await uniweb.wallet.current();
await uniweb.wallet.update({ merchantName?, merchantCity?, merchantCountry?, webhookUrl? });
await uniweb.webhooks.set(url); // overwrites the wallet-level callback URL
await uniweb.webhooks.info();
await uniweb.webhooks.remove();
await uniweb.webhooks.rollSecret(); // rotates the shared whsec_
Ordinary project routes must not call webhooks.set / remove / rollSecret or wallet.update — they mutate the wallet callback fallback and signing secret shared across all of the user's projects. Generate them only when the user explicitly asks for wallet administration and the route has project/admin-level authorization. Same for refunds, subscription mutations, payouts, KYC, and bank account APIs: generate only when the user explicitly requests that business flow and the code has validation, persistence, and authorization.
Webhook Integration
Callback URL: set it on the link/product, pointing at this Worker
Event delivery precedence: per-link webhookUrl > per-product webhookUrl > wallet-level fallback. The signing secret is always the wallet-level whsec_ (i.e. env.UNIWEB_WEBHOOK_SECRET).
PinMe sets a managed fallback callback URL on the wallet, but it exists only to obtain and preserve the signing secret — PinMe's server discards events it receives there (204); it never forwards them to the Worker. Business events must therefore set this project's webhookUrl explicitly on the resource that creates the payment:
- Payment links: pass
webhookUrlonlinks.create. - Checkout sessions:
checkout.createhas nowebhookUrlfield; events route through the price's product — setwebhookUrlonproducts.create(or on the reused product).
Rules for building the webhookUrl:
- Keep the callback path in a single constant (e.g.
const WEBHOOK_PATH = "/api/pay/webhook") shared by the router and thewebhookUrlconstruction, so a path mismatch can't 404 the callbacks and leave orders stuck in pending. - Use
env.WORKER_URLas the base:new URL(WEBHOOK_PATH, env.WORKER_URL).toString(). It is the only public address available at runtime (the platform subdomain); the user's custom domain is not inenv. Prefer it overrequest.url(the current reques
Truncated for display — read the full file on GitHub.
Related Skills
headroom
74.0kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ruflo
73.4k🌊 The original agent harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, federation, vector RAG integration, and native Claude Code / Codex / Hermes and many more Integrated
siyuan
46.5kAn open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与智能体在此协作
algorithmic-art
177.9kCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
