Pvium GitHub App
Reward GitHub contributors with Pvium payment links.
Turn merged pull requests into payable rewards. Maintainers label bounty issues, contributors close them with PRs, and Pvium handles the invite, payment link, funded webhook, and paid status updates.
/api/github/webhook/api/pvium/webhook/api/pvium/oauth/callbackFlow
- A repository owner installs the GitHub App.
- A maintainer labels an issue with a Pvium bounty label, for example pvium:20USDC or pvium:10.
- When a pull request is merged into a configured reward target branch and closes that issue, the app checks the PR author.
- If the PR author is already linked to a Pvium account, the app creates a Pvium payment link and comments a Pay reward link on the PR.
- If the PR author is not linked, the app generates a signed Pvium OAuth invite for type: github and comments the invite link on the PR.
- Once the user accepts the invite and connects the matching GitHub account in Pvium, the Pvium webhook creates the payment link and comments the Pay reward link on the PR.
- When Pvium sends a paid or funded webhook, the app marks the reward and bounty as PAID.
Local Setup
The reward automation uses the local Pvium SDK at /Users/Projects/Javascript/paytrack/sdks/node. The package points @pvium/sdk at file:../sdks/node, so rebuild the SDK after changing it.
cd /Users/Projects/Javascript/paytrack/sdks/node npm install npm run build cd /Users/Projects/Javascript/paytrack/github-app npm install cp .env.example .env npm run prisma:generate npm run prisma:migrate npm run dev
Environment
Required values are documented in .env.example:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/pvium_github_app" GITHUB_APP_ID="" GITHUB_APP_PRIVATE_KEY="" GITHUB_WEBHOOK_SECRET="" GITHUB_REWARD_TARGET_BRANCHES="main,master" PVIUM_BOUNTY_LABEL_PREFIX="pvium:" PVIUM_ENVIRONMENT="sandbox" PVIUM_API_BASE_URL="" PVIUM_CONSENT_HOST="" PVIUM_SDK_LOG_REQUESTS="false" PVIUM_API_KEY="" PVIUM_CLIENT_ID="" PVIUM_WEBHOOK_SECRET="" PVIUM_INVITE_SIGNER_PRIVATE_KEY="" PVIUM_OAUTH_REDIRECT_URI="http://localhost:3000/api/pvium/oauth/callback" PVIUM_REWARD_PAYMENT_MODEL="instant-batch" PVIUM_REWARD_PAYMENT_SIGNER_PRIVATE_KEY="" PVIUM_REWARD_PAYMENT_CHAIN="base" PVIUM_REWARD_PAYMENT_CHAIN_ID="8453" PVIUM_REWARD_PAYMENT_CURRENCY="USDC" PVIUM_REWARD_PAYMENT_TOKEN_ADDRESS="" PVIUM_REWARD_PAYMENT_TOKEN_DECIMALS="6" PVIUM_REWARD_PLATFORM_FEE_WALLET="" PVIUM_REWARD_PLATFORM_FEE_BASIS_POINTS="0" PVIUM_REWARD_MAX_FEE_AMOUNT="0" PVIUM_INVOICE_REDIRECT_URI="http://localhost:3000/api/pvium/oauth/callback" APP_BASE_URL="http://localhost:3000"
GitHub App
Generate GITHUB_APP_PRIVATE_KEY from the GitHub App settings page under Private keys, then copy the full PEM contents into the environment with line breaks replaced by \n.
Configure the webhook URL as https://<your-host>/api/github/webhook.
Repository Permissions
- Issues: read and write
- Pull requests: read and write
- Metadata: read-only
Subscribed Events
- issues
- pull_request
Pvium Configuration
Configure the Pvium webhook URL as https://<your-host>/api/pvium/webhook. Set PVIUM_WEBHOOK_SECRET to the same secret configured on the Pvium client app.
- GITHUB_REWARD_TARGET_BRANCHES is a comma-separated list of base branches that can trigger reward processing when a PR is merged.
- PVIUM_BOUNTY_LABEL_PREFIX controls the GitHub issue label prefix used to detect bounties. It defaults to pvium: when unset or empty.
- PVIUM_ENVIRONMENT resolves Pvium hosts: test uses localhost, sandbox uses api-sandbox.pvium.com, and production uses api.pvium.com.
- PVIUM_API_BASE_URL and PVIUM_CONSENT_HOST override the resolved Pvium hosts when needed.
- PVIUM_SDK_LOG_REQUESTS=true logs SDK request method, host, path, status, duration, and network errors without logging full URLs or secrets.
- PVIUM_REWARD_PAYMENT_MODEL controls the payment artifact. Use instant-batch for finalized instant batch payment links or invoice for the legacy invoice flow.
- PVIUM_REWARD_PAYMENT_CHAIN is the chain used by both invoice payment channels and instant batch links.
- PVIUM_REWARD_PAYMENT_CURRENCY is the invoice payment currency.
- PVIUM_REWARD_PAYMENT_SIGNER_PRIVATE_KEY signs instant batches. If omitted, the invite signer is used.
- PVIUM_REWARD_PAYMENT_CHAIN_ID is the chain id used to finalize instant batches.
- PVIUM_REWARD_PAYMENT_TOKEN_ADDRESS and PVIUM_REWARD_PAYMENT_TOKEN_DECIMALS define the instant batch payout token.
- PVIUM_REWARD_PLATFORM_FEE_WALLET receives the platform fee. If omitted, no platform fee payee is added.
- PVIUM_REWARD_PLATFORM_FEE_BASIS_POINTS sets the fee. For example, 100 is 1% and 250 is 2.5%.
- PVIUM_REWARD_MAX_FEE_AMOUNT caps the computed platform fee. Use 0 for no cap.
When PVIUM_REWARD_PLATFORM_FEE_WALLET is set and the fee basis points are greater than zero, instant batches include the platform fee as the first payee with memo platform fee. The contributor reward amount is not reduced by the fee.
Handled Events
GitHub
- issues.labeled
- pull_request.closed
Pvium
- oauth.invite.accepted
- invoice.paid
- invoice.payment_completed
- invoice.payment.succeeded
- payment.attached
- batch.funded
- batch.payment_completed
- batch.payment.succeeded
Usage
- Install the GitHub App on a repository.
- Add a bounty label to an issue, such as pvium:20USDC or pvium:20. If PVIUM_BOUNTY_LABEL_PREFIX is changed, use that prefix instead.
- Merge a PR into a configured reward target branch with a closing reference like Closes #123.
- The app comments on the merged PR.
- If the contributor needs to link Pvium, they use the invite link in the comment.
- Pvium redirects back to /api/pvium/oauth/callback with an OAuth code.
- The app exchanges the code through the local Pvium SDK, verifies the accepted GitHub handle, saves the OAuth token set, creates the payment link, and comments a Pay reward link.
- The maintainer clicks Pay reward and completes payment in Pvium.
The app stores Pvium OAuth access and refresh tokens on the GitHub user link so future merged PRs for the same contributor can create rewards without asking the contributor to authorize again. Treat these OAuth tokens as secrets; production deployments should encrypt them at rest and restrict database access.