End-to-end install and configuration walkthrough for workflow-plugin-payments.
# In your workflow project directory (the one with app.yaml)
wfctl plugin install workflow-plugin-paymentswfctl plugin install reads from workflow-registry, downloads the matching release binary from GitHub, and extracts it to data/plugins/payments/. It also writes a wfctl.yaml entry + .wfctl-lock.yaml line so subsequent wfctl plugin install calls (e.g. in CI) are deterministic.
Pin a specific version (replace <tag> with a real release tag):
wfctl plugin install workflow-plugin-payments@<tag>In CI, install is usually delegated to a composite action that reads the lockfile and bulk-installs all declared plugins:
- uses: ./.github/actions/setup-plugins
with:
app-id: ${{ vars.PLUGIN_INSTALLER_APP_ID }}
app-private-key: ${{ secrets.PLUGIN_INSTALLER_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}Add a payments.provider module to your app.yaml. The module name (stripe below) is what pipeline steps reference via the step's module: config field.
modules:
- name: stripe
type: payments.provider
config:
provider: stripe
secretKey: '{{config "stripe_secret_key"}}' # sk_live_… / sk_test_…
webhookSecret: '{{config "stripe_webhook_secret"}}' # whsec_… (charges/disputes endpoint)
defaultCurrency: usd # optional, defaults to "usd"secretKey is intentionally optional at module init: an empty value loads successfully and only fails at API call sites with payments.ErrStripeKeyMissing. Use this when the same app.yaml runs in environments where Stripe is intentionally disabled (local dev, ephemeral preview deploys).
modules:
- name: paypal
type: payments.provider
config:
provider: paypal
clientID: '{{config "paypal_client_id"}}'
clientSecret: '{{config "paypal_client_secret"}}'
webhookID: '{{config "paypal_webhook_id"}}' # required for VerifyWebhook
mode: live # "live" or "sandbox"Run multiple payments.provider modules side by side under different names:
modules:
- name: stripe
type: payments.provider
config: {provider: stripe, secretKey: '{{config "stripe_secret_key"}}'}
- name: paypal
type: payments.provider
config: {provider: paypal, clientID: '{{config "paypal_client_id"}}', clientSecret: '{{config "paypal_client_secret"}}'}Pipelines select between them via the step's module: stripe or module: paypal:
- name: charge_with_stripe
type: step.payment_charge
config:
module: stripe
amount: 1000
currency: usd
customer_id: '{{.steps.upsert.customer_id}}'The '{{config "stripe_secret_key"}}' expressions above resolve via the engine's config.provider module. Declare each key in the schema:
modules:
- name: config-provider
type: config.provider
config:
sources:
- type: defaults
- type: env
schema:
stripe_secret_key:
env: STRIPE_SECRET_KEY
required: false
sensitive: true
desc: "Stripe API secret key"
stripe_webhook_secret:
env: STRIPE_WEBHOOK_SECRET
required: false
sensitive: true
desc: "Stripe webhook signing secret (charges/disputes endpoint)"In production, source from a secrets-management plugin (workflow-plugin-secrets, AWS SecretsManager, GCP Secret Manager, Vault, etc.) instead of type: env. The provider reads the resolved value at module init.
wfctl validate app.yamlIf the validator reports unknown module type "payments.provider", the plugin install didn't land — check data/plugins/payments/plugin.json exists and wfctl plugin list shows the plugin. The plugin's binary is at data/plugins/payments/payments (renamed from the tarball's workflow-plugin-payments by wfctl plugin install's ensurePluginBinary step).
- wfctl version floor. Plugin-CLI commands (
wfctl payments …) require wfctl v0.27.5+. Earlier versions silently fail withunknown command: paymentsor fork/exec errors due to BuildCLIRegistry bugs that landed in#591/#595/#612/#613. Pinsetup-wfctl@v1accordingly in CI. - Plugin install cache. Composite actions usually cache
data/plugins/keyed byhashFiles('wfctl.yaml', '.wfctl-lock.yaml'). After upgrading the plugin, invalidate the cache (touch wfctl.yaml whitespace, or delete viagh api -X DELETE /repos/<owner>/<repo>/actions/caches/<id>); otherwisewfctl plugin installno-ops on a stale plugin and the new version'scliCommandsaren't picked up. - Cross-environment isolation. Stripe
pk_test_*/pk_live_*andsk_test_*/sk_live_*MUST be segregated per environment. Use environment-scoped GitHub secrets (or your secrets backend's equivalent) — never reuse a live secret across environments.