├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── feedback.md ├── PULL_REQUEST_TEMPLATE └── workflows │ ├── install-test.yml │ ├── release.yml │ ├── test-snapshot.yml │ └── test.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser ├── linux.yml ├── mac.yml └── windows.yml ├── .pre-commit ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── ARCHITECTURE.md ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── api ├── ZOOLANDER_SHA └── openapi-spec │ ├── README.md │ ├── spec3.sdk.json │ ├── spec3.v2.sdk.json │ └── spec3.v2.sdk.preview.json ├── cmd └── stripe │ └── main.go ├── docs ├── cli.svg ├── demo.gif └── terminal.svg ├── go.mod ├── go.sum ├── pkg ├── ansi │ ├── ansi.go │ └── init_windows.go ├── cmd │ ├── community.go │ ├── completion.go │ ├── config.go │ ├── daemon.go │ ├── delete.go │ ├── feedback.go │ ├── fixtures.go │ ├── get.go │ ├── listen.go │ ├── login.go │ ├── logout.go │ ├── logs.go │ ├── logs │ │ ├── tail.go │ │ └── tail_test.go │ ├── open.go │ ├── open_test.go │ ├── plugin.go │ ├── plugin │ │ ├── install.go │ │ ├── install_test.go │ │ ├── uninstall.go │ │ └── upgrade.go │ ├── plugin_cmds.go │ ├── plugin_cmds_test.go │ ├── post.go │ ├── postinstall.go │ ├── resource │ │ ├── alias.go │ │ ├── apps.go │ │ ├── events.go │ │ ├── events_resend.go │ │ ├── events_resend_test.go │ │ ├── namespace.go │ │ ├── namespace_test.go │ │ ├── operation.go │ │ ├── operation_test.go │ │ ├── resource.go │ │ ├── resource_test.go │ │ ├── terminal.go │ │ └── terminal_quickstart.go │ ├── resources.go │ ├── resources_cmds.go │ ├── resources_test.go │ ├── root.go │ ├── root_test.go │ ├── samples.go │ ├── samples │ │ ├── create.go │ │ └── list.go │ ├── serve.go │ ├── status.go │ ├── templates.go │ ├── templates_test.go │ ├── trigger.go │ └── version.go ├── config │ ├── config.go │ ├── config_test.go │ ├── profile.go │ └── profile_test.go ├── fixtures │ ├── fixtures.go │ ├── fixtures_test.go │ ├── triggers.go │ └── triggers │ │ ├── account.application.deauthorized.json │ │ ├── account.updated.json │ │ ├── balance.available.json │ │ ├── billing_portal.configuration.created.json │ │ ├── billing_portal.configuration.updated.json │ │ ├── billing_portal.session.created.json │ │ ├── cash_balance.funds_available.json │ │ ├── charge.captured.json │ │ ├── charge.dispute.closed.json │ │ ├── charge.dispute.created.json │ │ ├── charge.dispute.updated.json │ │ ├── charge.failed.json │ │ ├── charge.refund.updated.json │ │ ├── charge.refunded.json │ │ ├── charge.succeeded.json │ │ ├── checkout.session.async_payment_failed.json │ │ ├── checkout.session.async_payment_succeeded.json │ │ ├── checkout.session.completed.json │ │ ├── checkout.session.expired.json │ │ ├── coupon.created.json │ │ ├── coupon.deleted.json │ │ ├── coupon.updated.json │ │ ├── credit_note.created.json │ │ ├── credit_note.updated.json │ │ ├── credit_note.voided.json │ │ ├── customer.created.json │ │ ├── customer.deleted.json │ │ ├── customer.discount.created.json │ │ ├── customer.discount.deleted.json │ │ ├── customer.discount.updated.json │ │ ├── customer.source.created.json │ │ ├── customer.source.updated.json │ │ ├── customer.subscription.created.json │ │ ├── customer.subscription.deleted.json │ │ ├── customer.subscription.paused.json │ │ ├── customer.subscription.trial_will_end.json │ │ ├── customer.subscription.updated.json │ │ ├── customer.updated.json │ │ ├── customer_cash_balance_transaction.created.json │ │ ├── identity.verification_session.canceled.json │ │ ├── identity.verification_session.created.json │ │ ├── identity.verification_session.redacted.json │ │ ├── invoice.created.json │ │ ├── invoice.deleted.json │ │ ├── invoice.finalized.json │ │ ├── invoice.marked_uncollectible.json │ │ ├── invoice.paid.json │ │ ├── invoice.payment_action_required.json │ │ ├── invoice.payment_failed.json │ │ ├── invoice.payment_succeeded.json │ │ ├── invoice.sent.json │ │ ├── invoice.updated.json │ │ ├── invoice.voided.json │ │ ├── invoiceitem.created.json │ │ ├── invoiceitem.deleted.json │ │ ├── issuing_authorization.request.eu.json │ │ ├── issuing_authorization.request.gb.json │ │ ├── issuing_authorization.request.json │ │ ├── issuing_card.created.eu.json │ │ ├── issuing_card.created.gb.json │ │ ├── issuing_card.created.json │ │ ├── issuing_cardholder.created.eu.json │ │ ├── issuing_cardholder.created.gb.json │ │ ├── issuing_cardholder.created.json │ │ ├── payment_intent.amount_capturable_updated.json │ │ ├── payment_intent.canceled.json │ │ ├── payment_intent.created.json │ │ ├── payment_intent.partially_funded.json │ │ ├── payment_intent.payment_failed.json │ │ ├── payment_intent.processing.json │ │ ├── payment_intent.requires_action.json │ │ ├── payment_intent.succeeded.json │ │ ├── payment_link.created.json │ │ ├── payment_link.updated.json │ │ ├── payment_method.attached.json │ │ ├── payment_method.detached.json │ │ ├── payment_method.updated.json │ │ ├── payout.created.json │ │ ├── payout.updated.json │ │ ├── plan.created.json │ │ ├── plan.deleted.json │ │ ├── plan.updated.json │ │ ├── price.created.json │ │ ├── price.updated.json │ │ ├── product.created.json │ │ ├── product.deleted.json │ │ ├── product.updated.json │ │ ├── promotion_code.created.json │ │ ├── promotion_code.updated.json │ │ ├── quote.accepted.json │ │ ├── quote.canceled.json │ │ ├── quote.created.json │ │ ├── quote.finalized.json │ │ ├── reporting.report_run.succeeded.json │ │ ├── setup_intent.canceled.json │ │ ├── setup_intent.created.json │ │ ├── setup_intent.requires_action.json │ │ ├── setup_intent.setup_failed.json │ │ ├── setup_intent.succeeded.json │ │ ├── subscription.payment_failed.json │ │ ├── subscription.payment_succeeded.json │ │ ├── subscription_schedule.canceled.json │ │ ├── subscription_schedule.created.json │ │ ├── subscription_schedule.released.json │ │ ├── subscription_schedule.updated.json │ │ ├── tax_rate.created.json │ │ ├── tax_rate.updated.json │ │ ├── transfer.created.json │ │ ├── transfer.reversed.json │ │ ├── transfer.updated.json │ │ ├── v1.billing.meter.error_report_triggered.json │ │ └── v1.billing.meter.no_meter_found.json ├── gen │ ├── events_list.go.tpl │ ├── gen_events_list.go │ ├── gen_resources_cmds.go │ ├── resource_utils.go │ ├── resource_utils_test.go │ ├── resources_cmds.go.tpl │ └── stripe_version_header.go.tpl ├── git │ ├── editor.go │ ├── editor_test.go │ └── git.go ├── login │ ├── acct │ │ ├── retrieve_account.go │ │ └── retrieve_account_test.go │ ├── async.go │ ├── client_login.go │ ├── client_login_test.go │ ├── interactive_login.go │ ├── interactive_login_test.go │ ├── keys │ │ ├── configurer.go │ │ ├── configurer_test.go │ │ ├── keytransfer.go │ │ ├── keytransfer_test.go │ │ ├── polling.go │ │ └── polling_test.go │ ├── links.go │ ├── links_test.go │ ├── login.go │ ├── login_message.go │ └── login_message_test.go ├── logout │ └── logout.go ├── logtailing │ ├── tailer.go │ └── tailer_test.go ├── open │ └── open.go ├── parsers │ ├── parsers.go │ └── parsers_test.go ├── plugins │ ├── config_dev.go │ ├── interface.go │ ├── interface_grpc.go │ ├── plugin.go │ ├── plugin_test.go │ ├── proto │ │ ├── main.pb.go │ │ ├── main.proto │ │ └── main_grpc.pb.go │ ├── test_artifacts │ │ ├── plugins-2-merged-foo-1.toml │ │ ├── plugins-2.toml │ │ ├── plugins-3-merged-foo-1.toml │ │ ├── plugins-3.toml │ │ ├── plugins-foo-1.toml │ │ ├── plugins-merged-foo-1.toml │ │ ├── plugins.toml │ │ └── plugins_updated.toml │ ├── test_utils.go │ ├── utilities.go │ └── utilities_test.go ├── proxy │ ├── endpoint.go │ ├── endpoint_test.go │ ├── events_list.go │ ├── proxy.go │ ├── proxy_test.go │ ├── stripeevent.go │ ├── stripeevent_test.go │ ├── v2_stripe_event.go │ └── webhook_event_processor.go ├── requests │ ├── base.go │ ├── base_test.go │ ├── ids.go │ ├── plugin.go │ ├── request_tracer.go │ ├── stripe_version_header.go │ └── webhook_endpoints.go ├── rpcservice │ ├── events_resend.go │ ├── events_resend_test.go │ ├── fixtures.go │ ├── fixtures_test.go │ ├── listen.go │ ├── listen_test.go │ ├── login.go │ ├── login_status.go │ ├── login_status_test.go │ ├── login_test.go │ ├── logs_tail.go │ ├── logs_tail_test.go │ ├── middleware.go │ ├── middleware_test.go │ ├── rpc_service.go │ ├── rpc_service_test.go │ ├── sample_configs.go │ ├── sample_configs_test.go │ ├── sample_create.go │ ├── sample_create_test.go │ ├── samples_list.go │ ├── samples_list_test.go │ ├── trigger.go │ ├── trigger_test.go │ ├── triggers_list.go │ ├── triggers_list_test.go │ ├── version.go │ ├── version_test.go │ ├── webhook_endpoint_create.go │ └── webhook_endpoints_list.go ├── samples │ ├── create.go │ ├── list.go │ ├── os.go │ ├── os_test.go │ ├── samples.go │ └── samples_test.go ├── spec │ ├── spec.go │ └── spec_test.go ├── status │ ├── status.go │ └── status_test.go ├── stripe │ ├── analytics_telemetry.go │ ├── analytics_telemetry_test.go │ ├── client.go │ ├── client_test.go │ ├── url.go │ ├── url_test.go │ ├── verbosetransport.go │ └── verbosetransport_test.go ├── stripeauth │ ├── client.go │ ├── client_test.go │ └── messages.go ├── terminal │ ├── p400 │ │ ├── diagnostics.go │ │ ├── rabbit_service.go │ │ ├── rabbit_service_test.go │ │ ├── reader_constants.go │ │ ├── reader_errors.go │ │ ├── reader_methods.go │ │ ├── reader_methods_test.go │ │ ├── reader_payment.go │ │ ├── reader_registration.go │ │ ├── stripe_requests.go │ │ └── user_prompts.go │ ├── quickstart_p400.go │ ├── reader_list.go │ └── user_prompts.go ├── useragent │ ├── uname_unix.go │ ├── uname_unix_test.go │ ├── uname_windows.go │ └── useragent.go ├── validators │ ├── cmds.go │ ├── cmds_test.go │ ├── validate.go │ └── validate_test.go ├── version │ ├── version.go │ └── version_test.go └── websocket │ ├── client.go │ ├── client_test.go │ ├── messages.go │ ├── messages_test.go │ ├── request_log_messages.go │ ├── request_log_messages_test.go │ ├── visitor.go │ ├── webhook_messages.go │ └── webhook_messages_test.go ├── rpc ├── commands.pb.go ├── commands.proto ├── commands_grpc.pb.go ├── common.pb.go ├── common.proto ├── events_resend.pb.go ├── events_resend.proto ├── fixtures.pb.go ├── fixtures.proto ├── listen.pb.go ├── listen.proto ├── login.pb.go ├── login.proto ├── login_status.pb.go ├── login_status.proto ├── logs_tail.pb.go ├── logs_tail.proto ├── sample_configs.pb.go ├── sample_configs.proto ├── sample_create.pb.go ├── sample_create.proto ├── samples_list.pb.go ├── samples_list.proto ├── trigger.pb.go ├── trigger.proto ├── triggers_list.pb.go ├── triggers_list.proto ├── version.pb.go ├── version.proto ├── webhook_endpoint_create.pb.go ├── webhook_endpoint_create.proto ├── webhook_endpoints_list.pb.go └── webhook_endpoints_list.proto └── scripts ├── install-test.sh ├── postinstall.sh ├── publish-to-artifactory.sh ├── sync-openapi-v2.sh └── upload-zip-to-virustotal.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | ; http://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | 11 | [*.go] 12 | indent_style = tab 13 | indent_size = 4 14 | 15 | [*.{json,md}] 16 | indent_style = space 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *_vfsdata.go linguist-generated 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | _The more information we have the easier it is for us to help. Feel free to remove any sections that might not apply_ 11 | 12 | ### Issue 13 | _Describe what happened and what you were trying to do_ 14 | 15 | ### Expected Behavior 16 | _Tell us what you expected to happen_ 17 | 18 | ### Steps to reproduce 19 | _What are the steps we can take to reproduce this and verify it's fixed?_ 20 | 21 | ### Traceback 22 | _Share any debug output that was given by the CLI_ 23 | 24 | ### Environment 25 | _Select one of: macOS, Linux, Windows, Other_ 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Problem 11 | _What are you trying to do?_ 12 | 13 | ### Feature 14 | _What would help you accomplish your original task_ 15 | 16 | ### Examples 17 | _Any example interaction you expect_ 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feedback.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feedback 3 | about: Provide free-form feedback on the CLI 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Feedback 11 | _Give us free-form feedback on the CLI. Tell us what you're building, what you're trying to do with the CLI, features you'd like to see, areas that the CLI helps, areas where it needs improvement, anything confusing you saw, etc._ 12 | 13 | _Feedback can be completely freeform and stream of consciousness! If you prefer to submit private feedback, we have a form setup to do that: https://stri.pe/cli-feedback_ 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | ### Reviewers 2 | r? @ 3 | cc @stripe/developer-products 4 | 5 | ### Summary 6 | 7 | -------------------------------------------------------------------------------- /.github/workflows/test-snapshot.yml: -------------------------------------------------------------------------------- 1 | # Running goreleaser with the snapshot flag allows us to run some custom validations 2 | # on our build without publishing: https://goreleaser.com/customization/snapshots/ 3 | 4 | name: test-snapshot 5 | 6 | on: 7 | push: 8 | paths: 9 | - '.goreleaser/**' 10 | pull_request: 11 | paths: 12 | - 'goreleaser/**' 13 | 14 | jobs: 15 | build-linux: 16 | runs-on: ubuntu-latest 17 | env: 18 | # https://goreleaser.com/customization/docker_manifest/ 19 | DOCKER_CLI_EXPERIMENTAL: "enabled" 20 | 21 | steps: 22 | - name: Code checkout 23 | uses: actions/checkout@v2 24 | with: 25 | fetch-depth: 0 26 | - 27 | name: Docker Login 28 | env: 29 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 30 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} 31 | run: | 32 | echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin 33 | - name: Set up Docker Buildx 34 | uses: docker/setup-buildx-action@v2 35 | - name: Set up Go 36 | uses: actions/setup-go@v5 37 | with: 38 | go-version: 1.24.1 39 | - name: Run GoReleaser Snapshot 40 | uses: goreleaser/goreleaser-action@v6 41 | with: 42 | distribution: goreleaser 43 | version: "~> v2" 44 | args: release -f .goreleaser/linux.yml --clean --snapshot 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | ARTIFACTORY_SECRET: ${{ secrets.ARTIFACTORY_SECRET }} 48 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: Test 3 | jobs: 4 | test: 5 | strategy: 6 | matrix: 7 | go-version: [1.24.1] 8 | platform: [ubuntu-latest, macos-latest, windows-latest] 9 | runs-on: ${{ matrix.platform }} 10 | steps: 11 | - name: Install Go 12 | uses: actions/setup-go@v2 13 | with: 14 | go-version: ${{ matrix.go-version }} 15 | # Windows throws false positives with linting because of CRLF / goimports incompat 16 | - name: Set git to use LF 17 | run: | 18 | git config --global core.autocrlf false 19 | git config --global core.eol lf 20 | - name: Checkout code 21 | uses: actions/checkout@v2 22 | - name: Run Setup 23 | run: make setup 24 | - name: install diffutils 25 | if: runner.os == 'macOS' 26 | run: brew install diffutils 27 | - name: Install protoc 28 | uses: arduino/setup-protoc@v3 29 | with: 30 | repo-token: ${{ secrets.GITHUB_TOKEN }} 31 | - name: Install protoc deps 32 | run: | 33 | go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 34 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 35 | go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@v1.5.1 36 | shell: bash 37 | - name: golangci-lint 38 | uses: golangci/golangci-lint-action@v6 39 | with: 40 | version: v1.64.2 41 | args: --timeout=3m 42 | - name: Run Tests 43 | run: make ci 44 | shell: bash 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | bin/ 3 | coverage.txt 4 | /stripe 5 | /stripe-darwin 6 | /stripe-linux 7 | /stripe-windows.exe 8 | default_cassette.yaml 9 | .vscode/*.log 10 | stripe-completion.bash 11 | stripe-completion.zsh 12 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | tests: true 3 | 4 | linters: 5 | disable-all: true 6 | enable: 7 | - bodyclose 8 | - depguard 9 | - dogsled 10 | - dupl 11 | - gocritic 12 | - gocyclo 13 | - gofmt 14 | - goimports 15 | - goprintffuncname 16 | - gosimple 17 | - govet 18 | - ineffassign 19 | - misspell 20 | - nakedret 21 | # - revive -- TODO: reenable after fixing errors 22 | # - rowserrcheck -- disabled because of https://github.com/golangci/golangci-lint/issues/2649 23 | - staticcheck 24 | # - structcheck -- disabled because of https://github.com/golangci/golangci-lint/issues/2649 25 | - typecheck 26 | - unconvert 27 | - unused 28 | - whitespace 29 | 30 | linters-settings: 31 | depguard: 32 | rules: 33 | main: 34 | list-mode: lax 35 | allow: 36 | - $all 37 | deny: [] 38 | goimports: 39 | local-prefixes: github.com/stripe/stripe-cli 40 | govet: 41 | disable: 42 | - printf 43 | misspell: 44 | locale: US 45 | staticcheck: 46 | checks: 47 | - all 48 | - -SA1006 49 | - -SA1019 50 | 51 | issues: 52 | exclude-rules: 53 | - path: _test\.go 54 | linters: 55 | - dupl 56 | exclude-use-default: false 57 | max-issues-per-linter: 0 58 | max-same-issues: 0 59 | -------------------------------------------------------------------------------- /.goreleaser/windows.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | env: 3 | - GO111MODULE=on 4 | before: 5 | hooks: 6 | - go mod download 7 | - go generate ./... 8 | project_name: stripe 9 | builds: 10 | - id: stripe-windows 11 | ldflags: 12 | - -s -w -X github.com/stripe/stripe-cli/pkg/version.Version={{.Version}} 13 | binary: stripe 14 | env: 15 | - CGO_ENABLED=1 16 | - CC=x86_64-w64-mingw32-gcc 17 | - CXX=x86_64-w64-mingw32-g++ 18 | main: ./cmd/stripe/main.go 19 | goos: 20 | - windows 21 | goarch: 22 | - amd64 23 | - 386 24 | archives: 25 | - name_template: >- 26 | {{ .ProjectName }}_{{ .Version }}_{{ .Os }}_ 27 | {{- if eq .Arch "386" }}i386 28 | {{- else if eq .Arch "amd64" }}x86_64 29 | {{- else }}{{ .Arch }}{{ end }} 30 | format_overrides: 31 | - goos: windows 32 | formats: zip 33 | files: 34 | - none* 35 | changelog: 36 | sort: asc 37 | filters: 38 | exclude: 39 | - "^docs:" 40 | - "^test:" 41 | checksum: 42 | name_template: "{{ .ProjectName }}-windows-checksums.txt" 43 | snapshot: 44 | version_template: "{{ .Version }}-next" 45 | scoops: 46 | - repository: 47 | owner: stripe 48 | name: scoop-stripe-cli 49 | token: "{{ .Env.GORELEASER_GITHUB_TOKEN }}" # This token can access the repo, but GITHUB_TOKEN cannot 50 | commit_author: 51 | name: stripe-ci 52 | email: support@stripe.com 53 | homepage: https://stripe.com 54 | description: Stripe CLI utility 55 | license: Apache 2.0 56 | -------------------------------------------------------------------------------- /.pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # for this to work either manually copy to .git/hooks/pre-commit 4 | # or run `make githooks-init` 5 | 6 | # run linters before committing to give faster feedback to our developers 7 | make lint 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | // Go language plugin 4 | "golang.go", 5 | 6 | // Highly recommended 7 | "eamodio.gitlens", 8 | "ziyasal.vscode-open-in-github", 9 | "EditorConfig.editorconfig", 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Show the path in the top window bar. 3 | "window.title": "${rootName}${separator}${activeEditorMedium}", 4 | 5 | "editor.formatOnSave": false, 6 | 7 | "go.formatTool": "goimports", 8 | "go.formatFlags": [ 9 | "-local", "github.com/stripe/stripe-cli", 10 | ], 11 | "go.lintTool": "golangci-lint", 12 | "go.lintFlags": [ 13 | "--fast", 14 | ], 15 | "[go]": { 16 | "editor.formatOnSave": true 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @stripe/developer-products 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | RUN apk update && apk upgrade && \ 3 | apk add --no-cache ca-certificates 4 | COPY stripe /bin/stripe 5 | ENTRYPOINT ["/bin/stripe"] 6 | -------------------------------------------------------------------------------- /api/ZOOLANDER_SHA: -------------------------------------------------------------------------------- 1 | a3ba0bdb2d83fe3421fc76a42d5af881fe4fb53b 2 | -------------------------------------------------------------------------------- /api/openapi-spec/README.md: -------------------------------------------------------------------------------- 1 | # Stripe's OpenAPI Specification 2 | 3 | This folder contains an [OpenAPI specification](https://github.com/OAI/OpenAPI-Specification) for Stripe's API. 4 | -------------------------------------------------------------------------------- /cmd/stripe/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | "os" 7 | "time" 8 | 9 | "github.com/stripe/stripe-cli/pkg/cmd" 10 | "github.com/stripe/stripe-cli/pkg/stripe" 11 | ) 12 | 13 | func main() { 14 | ctx := context.Background() 15 | 16 | if stripe.TelemetryOptedOut(os.Getenv("STRIPE_CLI_TELEMETRY_OPTOUT")) || stripe.TelemetryOptedOut(os.Getenv("DO_NOT_TRACK")) { 17 | // Proceed without the telemetry client if client opted out. 18 | cmd.Execute(ctx) 19 | } else { 20 | // Set up the telemetry client and add it to the context 21 | httpClient := &http.Client{ 22 | Timeout: time.Second * 3, 23 | } 24 | telemetryClient := &stripe.AnalyticsTelemetryClient{HTTPClient: httpClient} 25 | contextWithTelemetry := stripe.WithTelemetryClient(ctx, telemetryClient) 26 | 27 | cmd.Execute(contextWithTelemetry) 28 | 29 | // Wait for all telemetry calls to finish before existing the process 30 | telemetryClient.Wait() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stripe/stripe-cli/e3020d2e2df9c731b2f51df3aa53bf16383e863f/docs/demo.gif -------------------------------------------------------------------------------- /pkg/ansi/init_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package ansi 5 | 6 | import ( 7 | "os" 8 | 9 | "golang.org/x/sys/windows" 10 | ) 11 | 12 | // enableAnsiColors enables support for ANSI color sequences in Windows 13 | // default console. Note that this only works with Windows 10. 14 | func enableAnsiColors() { 15 | stdout := windows.Handle(os.Stdout.Fd()) 16 | var originalMode uint32 17 | 18 | windows.GetConsoleMode(stdout, &originalMode) 19 | windows.SetConsoleMode(stdout, originalMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) 20 | } 21 | 22 | func init() { 23 | enableAnsiColors() 24 | } 25 | -------------------------------------------------------------------------------- /pkg/cmd/community.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/stripe/stripe-cli/pkg/open" 10 | ) 11 | 12 | const communityURL = "https://stripe.com/go/developer-chat" 13 | 14 | var openBrowser = open.Browser 15 | var canOpenBrowser = open.CanOpenBrowser 16 | 17 | type communityCmd struct { 18 | cmd *cobra.Command 19 | } 20 | 21 | func newCommunityCmd() *communityCmd { 22 | cc := &communityCmd{} 23 | 24 | cc.cmd = &cobra.Command{ 25 | Use: "community", 26 | Aliases: []string{"discord", "chat"}, 27 | Short: "Chat with Stripe engineers and other developers", 28 | Example: "stripe community", 29 | RunE: cc.runCommunityCmd, 30 | } 31 | 32 | return cc 33 | } 34 | 35 | func (cc *communityCmd) runCommunityCmd(cmd *cobra.Command, args []string) error { 36 | if !canOpenBrowser() { 37 | fmt.Printf("Chat with other developers and Stripe engineers in the official Stripe Discord server: %s\n", communityURL) 38 | return nil 39 | } 40 | 41 | fmt.Printf("Chat with other developers and Stripe engineers in the official Stripe Discord server.\n\nPress Enter to open the browser or visit %s", communityURL) 42 | 43 | input := os.Stdin 44 | fmt.Fscanln(input) 45 | 46 | err := openBrowser(communityURL) 47 | if err != nil { 48 | fmt.Printf("Failed to open browser, please go to %s manually.", communityURL) 49 | } 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/cmd/config.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/stripe/stripe-cli/pkg/config" 7 | ) 8 | 9 | type configCmd struct { 10 | cmd *cobra.Command 11 | config *config.Config 12 | 13 | list bool 14 | edit bool 15 | unset string 16 | set bool 17 | } 18 | 19 | func newConfigCmd() *configCmd { 20 | cc := &configCmd{ 21 | config: &Config, 22 | } 23 | cc.cmd = &cobra.Command{ 24 | Use: "config", 25 | Short: "Manually change the config values for the CLI", 26 | Long: `config lets you set and unset specific configuration values for your profile if 27 | you need more granular control over the configuration.`, 28 | Example: `stripe config --list 29 | stripe config --set color off 30 | stripe config --unset color`, 31 | RunE: cc.runConfigCmd, 32 | } 33 | 34 | cc.cmd.Flags().BoolVar(&cc.list, "list", false, "List configs") 35 | cc.cmd.Flags().BoolVarP(&cc.edit, "edit", "e", false, "Open an editor to the config file") 36 | cc.cmd.Flags().StringVar(&cc.unset, "unset", "", "Unset a specific config field") 37 | cc.cmd.Flags().BoolVar(&cc.set, "set", false, "Set a config field to some value") 38 | 39 | cc.cmd.Flags().SetInterspersed(false) // allow args to happen after flags to enable 2 arguments to --set 40 | 41 | return cc 42 | } 43 | 44 | func (cc *configCmd) runConfigCmd(cmd *cobra.Command, args []string) error { 45 | switch ok := true; ok { 46 | case cc.set && len(args) == 2: 47 | return cc.config.Profile.WriteConfigField(args[0], args[1]) 48 | case cc.unset != "": 49 | return cc.config.Profile.DeleteConfigField(cc.unset) 50 | case cc.list: 51 | return cc.config.PrintConfig() 52 | case cc.edit: 53 | return cc.config.EditConfig() 54 | default: 55 | // no flags set or unrecognized flags/args 56 | return cc.cmd.Help() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pkg/cmd/daemon.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/stripe/stripe-cli/pkg/config" 8 | "github.com/stripe/stripe-cli/pkg/rpcservice" 9 | "github.com/stripe/stripe-cli/pkg/stripe" 10 | "github.com/stripe/stripe-cli/pkg/validators" 11 | ) 12 | 13 | type daemonCmd struct { 14 | cmd *cobra.Command 15 | port int 16 | cfg *config.Config 17 | } 18 | 19 | func newDaemonCmd(cfg *config.Config) *daemonCmd { 20 | dc := &daemonCmd{ 21 | cfg: cfg, 22 | } 23 | 24 | dc.cmd = &cobra.Command{ 25 | Use: "daemon", 26 | Args: validators.NoArgs, 27 | Short: "Run as a daemon on your localhost", 28 | Long: `Start a local gRPC server, enabling you to invoke Stripe CLI commands programmatically from a gRPC 29 | client. 30 | 31 | Currently, stripe daemon only supports a subset of CLI commands. Documentation is not yet available.`, 32 | Run: dc.runDaemonCmd, 33 | Hidden: true, 34 | } 35 | dc.cmd.Flags().IntVar(&dc.port, "port", 0, "The TCP port the daemon will listen to (default: an available port)") 36 | 37 | return dc 38 | } 39 | 40 | func (dc *daemonCmd) runDaemonCmd(cmd *cobra.Command, args []string) { 41 | telemetryClient := stripe.GetTelemetryClient(cmd.Context()) 42 | srv := rpcservice.New(&rpcservice.Config{ 43 | Port: dc.port, 44 | Log: log.StandardLogger(), 45 | UserCfg: dc.cfg, 46 | }, telemetryClient) 47 | 48 | ctx := withSIGTERMCancel(cmd.Context(), func() { 49 | log.WithFields(log.Fields{ 50 | "prefix": "cmd.daemonCmd.runDaemonCmd", 51 | }).Debug("Ctrl+C received, cleaning up...") 52 | }) 53 | 54 | go srv.Run(ctx) 55 | 56 | <-ctx.Done() 57 | } 58 | -------------------------------------------------------------------------------- /pkg/cmd/delete.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/stripe/stripe-cli/pkg/requests" 9 | "github.com/stripe/stripe-cli/pkg/validators" 10 | ) 11 | 12 | type deleteCmd struct { 13 | reqs requests.Base 14 | } 15 | 16 | func newDeleteCmd() *deleteCmd { 17 | gc := &deleteCmd{} 18 | 19 | gc.reqs.Method = http.MethodDelete 20 | gc.reqs.Profile = &Config.Profile 21 | gc.reqs.Cmd = &cobra.Command{ 22 | Use: "delete ", 23 | Args: validators.ExactArgs(1), 24 | Short: "Make a DELETE request to the Stripe API", 25 | Long: `Make DELETE requests to the Stripe API using your test mode key. 26 | 27 | For a full list of supported paths, see the API reference: 28 | https://stripe.com/docs/api 29 | 30 | To delete a charge: 31 | 32 | $ stripe delete /customers/cus_FROPkgsHVRRspg`, 33 | RunE: gc.reqs.RunRequestsCmd, 34 | } 35 | 36 | gc.reqs.InitFlags() 37 | 38 | return gc 39 | } 40 | -------------------------------------------------------------------------------- /pkg/cmd/feedback.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/stripe/stripe-cli/pkg/validators" 9 | ) 10 | 11 | type feedbackCmd struct { 12 | cmd *cobra.Command 13 | } 14 | 15 | func newFeedbackdCmd() *feedbackCmd { 16 | return &feedbackCmd{ 17 | cmd: &cobra.Command{ 18 | Use: "feedback", 19 | Args: validators.NoArgs, 20 | Short: "Provide us with feedback on the CLI", 21 | Run: func(cmd *cobra.Command, args []string) { 22 | output := ` 23 | _ _ 24 | ___| |_ _ __(_)_ __ ___ 25 | / __| __| '__| | '_ \ / _ \ 26 | \__ \ |_| | | | |_) | __/ 27 | |___/\__|_| |_| .__/ \___| 28 | |_| 29 | 30 | We'd love to know what you think of the CLI: 31 | 32 | * Report bugs or issues on GitHub: https://github.com/stripe/stripe-cli/issues 33 | ` 34 | 35 | fmt.Println(output) 36 | }, 37 | }, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/cmd/get.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/stripe/stripe-cli/pkg/requests" 9 | "github.com/stripe/stripe-cli/pkg/validators" 10 | ) 11 | 12 | type getCmd struct { 13 | reqs requests.Base 14 | } 15 | 16 | func newGetCmd() *getCmd { 17 | gc := &getCmd{} 18 | 19 | gc.reqs.Method = http.MethodGet 20 | gc.reqs.Profile = &Config.Profile 21 | gc.reqs.Cmd = &cobra.Command{ 22 | Use: "get ", 23 | Args: validators.ExactArgs(1), 24 | Short: "Retrieve resources by their ID or make GET requests", 25 | Long: `With the get command, you can load API resources by providing just the resource 26 | id. You can also make normal HTTP GET requests to the Stripe API by providing 27 | the API path.`, 28 | Example: `stripe get ch_1EGYgUByst5pquEtjb0EkYha 29 | stripe get cus_G6GQwbr1dWXt9O 30 | stripe get /v1/charges --limit 50`, 31 | RunE: gc.reqs.RunRequestsCmd, 32 | } 33 | 34 | gc.reqs.InitFlags() 35 | 36 | return gc 37 | } 38 | -------------------------------------------------------------------------------- /pkg/cmd/login.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/stripe/stripe-cli/pkg/login" 7 | "github.com/stripe/stripe-cli/pkg/stripe" 8 | "github.com/stripe/stripe-cli/pkg/validators" 9 | ) 10 | 11 | type loginCmd struct { 12 | cmd *cobra.Command 13 | interactive bool 14 | dashboardBaseURL string 15 | } 16 | 17 | func newLoginCmd() *loginCmd { 18 | lc := &loginCmd{} 19 | 20 | lc.cmd = &cobra.Command{ 21 | Use: "login", 22 | Args: validators.NoArgs, 23 | Short: "Login to your Stripe account", 24 | Long: `Login to your Stripe account to setup the CLI`, 25 | RunE: lc.runLoginCmd, 26 | } 27 | lc.cmd.Flags().BoolVarP(&lc.interactive, "interactive", "i", false, "Run interactive configuration mode if you cannot open a browser") 28 | 29 | // Hidden configuration flags, useful for dev/debugging 30 | lc.cmd.Flags().StringVar(&lc.dashboardBaseURL, "dashboard-base", stripe.DefaultDashboardBaseURL, "Sets the dashboard base URL") 31 | lc.cmd.Flags().MarkHidden("dashboard-base") // #nosec G104 32 | 33 | return lc 34 | } 35 | 36 | func (lc *loginCmd) runLoginCmd(cmd *cobra.Command, args []string) error { 37 | if err := stripe.ValidateDashboardBaseURL(lc.dashboardBaseURL); err != nil { 38 | return err 39 | } 40 | 41 | if lc.interactive { 42 | return login.InteractiveLogin(cmd.Context(), &Config) 43 | } 44 | 45 | return login.Login(cmd.Context(), lc.dashboardBaseURL, &Config) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/cmd/logout.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/stripe/stripe-cli/pkg/logout" 7 | "github.com/stripe/stripe-cli/pkg/validators" 8 | ) 9 | 10 | type logoutCmd struct { 11 | cmd *cobra.Command 12 | all bool 13 | } 14 | 15 | func newLogoutCmd() *logoutCmd { 16 | lc := &logoutCmd{} 17 | 18 | lc.cmd = &cobra.Command{ 19 | Use: "logout", 20 | Args: validators.NoArgs, 21 | Short: "Logout of your Stripe account", 22 | Long: `Logout of your Stripe account from the CLI`, 23 | RunE: lc.runLogoutCmd, 24 | } 25 | 26 | lc.cmd.Flags().BoolVarP(&lc.all, "all", "a", false, "Clear credentials for all projects you are currently logged into.") 27 | 28 | return lc 29 | } 30 | 31 | func (lc *logoutCmd) runLogoutCmd(cmd *cobra.Command, args []string) error { 32 | if lc.all { 33 | return logout.All(&Config) 34 | } 35 | 36 | return logout.Logout(&Config) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/cmd/logs.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | logs "github.com/stripe/stripe-cli/pkg/cmd/logs" 7 | "github.com/stripe/stripe-cli/pkg/config" 8 | "github.com/stripe/stripe-cli/pkg/validators" 9 | ) 10 | 11 | // LogsCmd is a wrapper for the base logs command 12 | type LogsCmd struct { 13 | Cmd *cobra.Command 14 | cfg *config.Config 15 | } 16 | 17 | func newLogsCmd(config *config.Config) *LogsCmd { 18 | logsCmd := &LogsCmd{ 19 | cfg: config, 20 | } 21 | 22 | logsCmd.Cmd = &cobra.Command{ 23 | Use: "logs", 24 | Args: validators.NoArgs, 25 | Short: "Interact with Stripe API request logs", 26 | Long: `Tail Stripe API request logs in real-time and see debug information.`, 27 | } 28 | 29 | logsCmd.Cmd.AddCommand(logs.NewTailCmd(logsCmd.cfg).Cmd) 30 | 31 | return logsCmd 32 | } 33 | -------------------------------------------------------------------------------- /pkg/cmd/open_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestGetLongestShortcut(t *testing.T) { 10 | shortcuts := []string{"bender", "fry", "leela"} 11 | 12 | require.Equal(t, getLongestShortcut(shortcuts), 6) 13 | } 14 | 15 | func TestNamePadding(t *testing.T) { 16 | require.Equal(t, padName("fry", 6), "fry ") 17 | require.Equal(t, padName("leela", 6), "leela ") 18 | require.Equal(t, padName("bender", 6), "bender") 19 | } 20 | -------------------------------------------------------------------------------- /pkg/cmd/plugin.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/stripe/stripe-cli/pkg/cmd/plugin" 7 | "github.com/stripe/stripe-cli/pkg/validators" 8 | ) 9 | 10 | type pluginCmd struct { 11 | cmd *cobra.Command 12 | } 13 | 14 | func newPluginCmd() *pluginCmd { 15 | pc := &pluginCmd{} 16 | 17 | pc.cmd = &cobra.Command{ 18 | Use: "plugin", 19 | Hidden: true, 20 | Args: validators.ExactArgs(1), 21 | Short: "Interact with Stripe CLI plugins", 22 | Long: "Interact with Stripe CLI plugins.", 23 | } 24 | 25 | pc.cmd.AddCommand(plugin.NewInstallCmd(&Config).Cmd) 26 | pc.cmd.AddCommand(plugin.NewUpgradeCmd(&Config).Cmd) 27 | pc.cmd.AddCommand(plugin.NewUninstallCmd(&Config).Cmd) 28 | 29 | return pc 30 | } 31 | -------------------------------------------------------------------------------- /pkg/cmd/plugin/install_test.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestParseArg(t *testing.T) { 10 | // No version 11 | plugin, version := parseInstallArg("apps") 12 | require.Equal(t, "apps", plugin) 13 | require.Equal(t, "", version) 14 | 15 | // Version 16 | plugin, version = parseInstallArg("apps@2.0.1") 17 | require.Equal(t, "apps", plugin) 18 | require.Equal(t, "2.0.1", version) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/cmd/plugin/uninstall.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | 8 | log "github.com/sirupsen/logrus" 9 | "github.com/spf13/afero" 10 | "github.com/spf13/cobra" 11 | 12 | "github.com/stripe/stripe-cli/pkg/ansi" 13 | "github.com/stripe/stripe-cli/pkg/config" 14 | "github.com/stripe/stripe-cli/pkg/plugins" 15 | "github.com/stripe/stripe-cli/pkg/validators" 16 | ) 17 | 18 | // UninstallCmd is the struct used for configuring the plugin uninstall command 19 | type UninstallCmd struct { 20 | cfg *config.Config 21 | Cmd *cobra.Command 22 | fs afero.Fs 23 | } 24 | 25 | // NewUninstallCmd creates a new command for uninstalling plugins 26 | func NewUninstallCmd(config *config.Config) *UninstallCmd { 27 | uc := &UninstallCmd{} 28 | uc.fs = afero.NewOsFs() 29 | uc.cfg = config 30 | 31 | uc.Cmd = &cobra.Command{ 32 | Use: "uninstall", 33 | Args: validators.ExactArgs(1), 34 | Short: "Uninstall a Stripe CLI plugin", 35 | Long: "Uninstall a Stripe CLI plugin.", 36 | RunE: uc.runUninstallCmd, 37 | } 38 | 39 | return uc 40 | } 41 | 42 | func (uc *UninstallCmd) runUninstallCmd(cmd *cobra.Command, args []string) error { 43 | ctx := withSIGTERMCancel(cmd.Context(), func() { 44 | log.WithFields(log.Fields{ 45 | "prefix": "cmd.uninstallCmd.runUninstallCmd", 46 | }).Debug("Ctrl+C received, cleaning up...") 47 | }) 48 | 49 | plugin, err := plugins.LookUpPlugin(cmd.Context(), uc.cfg, uc.fs, args[0]) 50 | 51 | if err != nil { 52 | return errors.New("this plugin doesn't seem to exist") 53 | } 54 | 55 | err = plugin.Uninstall(ctx, uc.cfg, uc.fs) 56 | 57 | if err == nil { 58 | color := ansi.Color(os.Stdout) 59 | successMsg := fmt.Sprintf("✔ %s has been uninstalled.", plugin.Shortname) 60 | fmt.Println(color.Green(successMsg)) 61 | } 62 | 63 | return err 64 | } 65 | -------------------------------------------------------------------------------- /pkg/cmd/post.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/stripe/stripe-cli/pkg/requests" 9 | "github.com/stripe/stripe-cli/pkg/validators" 10 | ) 11 | 12 | type postCmd struct { 13 | reqs requests.Base 14 | } 15 | 16 | func newPostCmd() *postCmd { 17 | gc := &postCmd{} 18 | 19 | gc.reqs.Method = http.MethodPost 20 | gc.reqs.Profile = &Config.Profile 21 | gc.reqs.Cmd = &cobra.Command{ 22 | Use: "post ", 23 | Args: validators.ExactArgs(1), 24 | Short: "Make a POST request to the Stripe API", 25 | Long: `Make POST requests to the Stripe API using your test mode key. 26 | 27 | The post command supports API features like idempotency keys and expand flags. 28 | 29 | For a full list of supported paths, see the API reference: 30 | https://stripe.com/docs/api 31 | `, 32 | Example: `stripe post /payment_intents \ 33 | -d amount=2000 \ 34 | -d currency=usd \ 35 | -d "payment_method_types[]=card"`, 36 | RunE: gc.reqs.RunRequestsCmd, 37 | } 38 | 39 | gc.reqs.InitFlags() 40 | 41 | return gc 42 | } 43 | -------------------------------------------------------------------------------- /pkg/cmd/postinstall.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/stripe/stripe-cli/pkg/ansi" 10 | "github.com/stripe/stripe-cli/pkg/config" 11 | "github.com/stripe/stripe-cli/pkg/validators" 12 | ) 13 | 14 | type postinstallCmd struct { 15 | cfg *config.Config 16 | cmd *cobra.Command 17 | } 18 | 19 | func newPostinstallCmd(config *config.Config) *postinstallCmd { 20 | pic := &postinstallCmd{ 21 | cfg: config, 22 | } 23 | pic.cmd = &cobra.Command{ 24 | Use: "postinstall", 25 | Args: validators.NoArgs, 26 | Short: "Run some checks after installation of this CLI and prompt user if needed", 27 | Example: `stripe postinstall`, 28 | Hidden: true, 29 | RunE: pic.runPostinstallCmd, 30 | } 31 | return pic 32 | } 33 | 34 | func (pic *postinstallCmd) runPostinstallCmd(cmd *cobra.Command, args []string) error { 35 | color := ansi.Color(os.Stdout) 36 | _, err := pic.cfg.Profile.GetAPIKey(false) 37 | 38 | // If we can't get the API key, then it's likely that this is a first install rather than an upgrade. 39 | // Suggest the user run `stripe login` to get started as a helpful prompt. 40 | if err != nil { 41 | welcomeIcon := color.BrightRed("❤").String() 42 | welcomeText := "Thanks for installing the Stripe CLI! To get started, run `stripe login`" 43 | fmt.Printf("%s %s\n", welcomeIcon, welcomeText) 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /pkg/cmd/resource/alias.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // Aliases are mapped from resource name in the OpenAPI spec -> friendly name in the CLI 8 | // Each alias causes a second resource command (the target / friendly name) to be generated, and uses post-processing 9 | // to hide the principle resource name (OpenAPI spec) 10 | var aliasedCmds = map[string]string{ 11 | "line_item": "invoice_line_item", 12 | } 13 | 14 | // GetCmdAlias retrieves the alias for a given resource, if one is present; otherwise returns "" 15 | func GetCmdAlias(principle string) string { 16 | alias, ok := aliasedCmds[principle] 17 | if !ok { 18 | return "" 19 | } 20 | return alias 21 | } 22 | 23 | // GetAliases retrieves the entire alias map, useful for testing 24 | func GetAliases() map[string]string { 25 | return aliasedCmds 26 | } 27 | 28 | // HideAliasedCommands performs the post-processing on the command tree to hide 29 | // resources that have an alias 30 | func HideAliasedCommands(rootCmd *cobra.Command) { 31 | for _, cmd := range rootCmd.Commands() { 32 | for principle := range aliasedCmds { 33 | formattedPrinciple := GetResourceCmdName(principle) 34 | if cmd.Use == formattedPrinciple { 35 | cmd.Hidden = true 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/cmd/resource/apps.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // RemoveAppsCmd removes the autogenerated `apps` command as we need the plugin to have this namespace 8 | func RemoveAppsCmd(rootCmd *cobra.Command) error { 9 | for _, cmd := range rootCmd.Commands() { 10 | if cmd.Use == "apps" { 11 | // Remove the autogenerated in favor of the plugin 12 | rootCmd.RemoveCommand(cmd) 13 | break 14 | } 15 | } 16 | 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /pkg/cmd/resource/events.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/stripe/stripe-cli/pkg/config" 9 | ) 10 | 11 | // AddEventsSubCmds adds custom subcommands to the `events` command created 12 | // automatically as a resource command. 13 | func AddEventsSubCmds(rootCmd *cobra.Command, cfg *config.Config) error { 14 | found := false 15 | 16 | for _, cmd := range rootCmd.Commands() { 17 | if cmd.Use == "events" { 18 | found = true 19 | 20 | NewEventsResendCmd(cmd, cfg) 21 | 22 | break 23 | } 24 | } 25 | 26 | if !found { 27 | return errors.New("Could not find events command") 28 | } 29 | 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /pkg/cmd/resource/events_resend.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/stripe/stripe-cli/pkg/config" 9 | "github.com/stripe/stripe-cli/pkg/spec" 10 | ) 11 | 12 | // EventsResendCmd represents the event resend API operation command. This 13 | // command is manually defined because it has a custom behavior. 14 | type EventsResendCmd struct { 15 | opCmd *OperationCmd 16 | } 17 | 18 | func (erc *EventsResendCmd) runEventsResendCmd(cmd *cobra.Command, args []string) error { 19 | // If the `webhook-endpoint` flag was not passed, then add 20 | // `for_stripecli=true` to the request so the event is replayed to the 21 | // Stripe CLI. 22 | if !erc.opCmd.Cmd.Flags().Changed("webhook-endpoint") { 23 | erc.opCmd.Parameters.AppendData([]string{"for_stripecli=true"}) 24 | } 25 | 26 | return erc.opCmd.runOperationCmd(cmd, args) 27 | } 28 | 29 | // NewEventsResendCmd returns a new EventsResendCmd. 30 | func NewEventsResendCmd(parentCmd *cobra.Command, cfg *config.Config) *EventsResendCmd { 31 | eventsResendCmd := &EventsResendCmd{ 32 | opCmd: NewOperationCmd(parentCmd, "resend", "/v1/events/{event}/retry", http.MethodPost, map[string]string{ 33 | "account": "string", 34 | "webhook_endpoint": "string", 35 | }, map[string][]spec.StripeEnumValue{}, cfg, false), 36 | } 37 | 38 | eventsResendCmd.opCmd.Cmd.RunE = eventsResendCmd.runEventsResendCmd 39 | 40 | return eventsResendCmd 41 | } 42 | -------------------------------------------------------------------------------- /pkg/cmd/resource/namespace_test.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/spf13/cobra" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestNewNamespaceCmd_NonEmptyName(t *testing.T) { 11 | rootCmd := &cobra.Command{Annotations: make(map[string]string)} 12 | 13 | nsc := NewNamespaceCmd(rootCmd, "foo") 14 | 15 | require.Equal(t, "foo", nsc.Name) 16 | require.True(t, rootCmd.HasSubCommands()) 17 | val, ok := rootCmd.Annotations["foo"] 18 | require.True(t, ok) 19 | require.Equal(t, "namespace", val) 20 | require.Contains(t, nsc.Cmd.UsageTemplate(), "Available Resources") 21 | } 22 | 23 | func TestNewNamespaceCmd_EmptyName(t *testing.T) { 24 | rootCmd := &cobra.Command{Annotations: make(map[string]string)} 25 | 26 | nsc := NewNamespaceCmd(rootCmd, "") 27 | 28 | require.Equal(t, "", nsc.Name) 29 | require.False(t, rootCmd.HasSubCommands()) 30 | _, ok := rootCmd.Annotations[""] 31 | require.False(t, ok) 32 | require.Contains(t, nsc.Cmd.UsageTemplate(), "Available Resources") 33 | } 34 | -------------------------------------------------------------------------------- /pkg/cmd/resource/resource_test.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/spf13/cobra" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestNewResourceCmd(t *testing.T) { 11 | parentCmd := &cobra.Command{Annotations: make(map[string]string)} 12 | 13 | rc := NewResourceCmd(parentCmd, "foo") 14 | 15 | require.Equal(t, "foo", rc.Name) 16 | require.True(t, parentCmd.HasSubCommands()) 17 | val, ok := parentCmd.Annotations["foo"] 18 | require.True(t, ok) 19 | require.Equal(t, "resource", val) 20 | require.Contains(t, rc.Cmd.UsageTemplate(), "Available Operations") 21 | } 22 | -------------------------------------------------------------------------------- /pkg/cmd/resource/terminal.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/stripe/stripe-cli/pkg/config" 9 | ) 10 | 11 | // AddTerminalSubCmds adds custom subcommands to the `terminal` command created 12 | // automatically as a resource command. 13 | func AddTerminalSubCmds(rootCmd *cobra.Command, cfg *config.Config) error { 14 | found := false 15 | 16 | for _, cmd := range rootCmd.Commands() { 17 | if cmd.Use == "terminal" { 18 | found = true 19 | 20 | NewQuickstartCmd(cmd, cfg) 21 | 22 | break 23 | } 24 | } 25 | 26 | if !found { 27 | return errors.New("Could not find terminal command") 28 | } 29 | 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /pkg/cmd/resources.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/stripe/stripe-cli/pkg/ansi" 9 | "github.com/stripe/stripe-cli/pkg/validators" 10 | ) 11 | 12 | type resourcesCmd struct { 13 | cmd *cobra.Command 14 | } 15 | 16 | func newResourcesCmd() *resourcesCmd { 17 | rc := &resourcesCmd{} 18 | 19 | rc.cmd = &cobra.Command{ 20 | Use: "resources", 21 | Args: validators.NoArgs, 22 | Short: "List resource commands", 23 | } 24 | rc.cmd.SetHelpTemplate(getResourcesHelpTemplate()) 25 | 26 | return rc 27 | } 28 | 29 | func getResourcesHelpTemplate() string { 30 | // This template uses `.Parent` to access subcommands on the root command. 31 | return fmt.Sprintf(`%s{{range $index, $cmd := .Parent.Commands}}{{if (and (not $cmd.Hidden) (or (eq (index $.Parent.Annotations $cmd.Name) "resource") (eq (index $.Parent.Annotations $cmd.Name) "namespace")))}} 32 | {{rpad $cmd.Name $cmd.NamePadding }} {{$cmd.Short}}{{end}}{{end}} 33 | 34 | Use "stripe [command] --help" for more information about a command. 35 | `, 36 | ansi.Bold("Available commands:"), 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/cmd/samples.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/stripe/stripe-cli/pkg/cmd/samples" 7 | ) 8 | 9 | type samplesCmd struct { 10 | cmd *cobra.Command 11 | } 12 | 13 | func newSamplesCmd() *samplesCmd { 14 | samplesCmd := &samplesCmd{ 15 | cmd: &cobra.Command{ 16 | Use: "samples", 17 | Short: `Sample integrations built by Stripe`, 18 | Long: ``, 19 | }, 20 | } 21 | 22 | samplesCmd.cmd.AddCommand(samples.NewCreateCmd(&Config).Cmd) 23 | samplesCmd.cmd.AddCommand(samples.NewListCmd().Cmd) 24 | 25 | return samplesCmd 26 | } 27 | -------------------------------------------------------------------------------- /pkg/cmd/samples/list.go: -------------------------------------------------------------------------------- 1 | package samples 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sort" 7 | 8 | "github.com/spf13/cobra" 9 | 10 | "github.com/stripe/stripe-cli/pkg/ansi" 11 | "github.com/stripe/stripe-cli/pkg/samples" 12 | "github.com/stripe/stripe-cli/pkg/validators" 13 | ) 14 | 15 | // ListCmd prints a list of all the available sample projects that users can 16 | // generate 17 | type ListCmd struct { 18 | Cmd *cobra.Command 19 | } 20 | 21 | // NewListCmd creates and returns a list command for samples 22 | func NewListCmd() *ListCmd { 23 | listCmd := &ListCmd{} 24 | listCmd.Cmd = &cobra.Command{ 25 | Use: "list", 26 | Args: validators.NoArgs, 27 | Short: "List Stripe Samples supported by the CLI", 28 | Long: `A list of available Stripe Sample integrations that can be setup and bootstrap by 29 | the CLI.`, 30 | RunE: listCmd.runListCmd, 31 | } 32 | 33 | return listCmd 34 | } 35 | 36 | func (lc *ListCmd) runListCmd(cmd *cobra.Command, args []string) error { 37 | fmt.Println("A list of available Stripe Samples:") 38 | fmt.Println() 39 | 40 | spinner := ansi.StartNewSpinner("Loading...", os.Stdout) 41 | 42 | list, err := samples.GetSamples("list") 43 | if err != nil { 44 | ansi.StopSpinner(spinner, "Error: please check your internet connection and try again!", os.Stdout) 45 | return err 46 | } 47 | ansi.StopSpinner(spinner, "", os.Stdout) 48 | 49 | names := samples.Names(list) 50 | sort.Strings(names) 51 | 52 | for _, name := range names { 53 | fmt.Println(list[name].BoldName()) 54 | fmt.Println(list[name].Description) 55 | fmt.Printf("Repo: %s\n", list[name].URL) 56 | fmt.Println() 57 | } 58 | 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /pkg/cmd/serve.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/gorilla/handlers" 10 | "github.com/spf13/cobra" 11 | 12 | "github.com/stripe/stripe-cli/pkg/validators" 13 | ) 14 | 15 | type serveCmd struct { 16 | cmd *cobra.Command 17 | } 18 | 19 | func newServeCmd() *serveCmd { 20 | var port string 21 | 22 | sc := &serveCmd{} 23 | 24 | sc.cmd = &cobra.Command{ 25 | Use: "serve", 26 | Aliases: []string{"srv"}, 27 | Short: "Serve static files locally", 28 | Args: validators.MaximumNArgs(1), 29 | Example: "stripe serve /path/to/directory", 30 | RunE: func(cmd *cobra.Command, args []string) error { 31 | dir := "." 32 | if len(args) == 1 { 33 | dir = args[0] 34 | } 35 | 36 | absoluteDir, err := filepath.Abs(dir) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | fmt.Printf("Starting server for directory %s\n", absoluteDir) 42 | 43 | fmt.Println("Starting static file server at address", fmt.Sprintf("http://localhost:%s", port)) 44 | http.Handle("/", http.FileServer(http.Dir(absoluteDir))) 45 | return http.ListenAndServe(fmt.Sprintf("localhost:%s", port), handlers.LoggingHandler(os.Stdout, http.DefaultServeMux)) 46 | }, 47 | } 48 | 49 | sc.cmd.Flags().StringVar(&port, "port", "4242", "Provide a custom port to serve content from.") 50 | 51 | return sc 52 | } 53 | -------------------------------------------------------------------------------- /pkg/cmd/templates_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/spf13/afero" 9 | "github.com/stretchr/testify/assert" 10 | 11 | "github.com/stripe/stripe-cli/pkg/config" 12 | ) 13 | 14 | func TestGetLogin(t *testing.T) { 15 | fs := afero.NewMemMapFs() 16 | cfg := config.Config{} 17 | expected := ` 18 | Before using the CLI, you'll need to login: 19 | 20 | $ stripe login 21 | 22 | If you're working on multiple projects, you can run the login command with the 23 | --project-name flag: 24 | 25 | $ stripe login --project-name rocket-rides` 26 | output := getLogin(&fs, &cfg) 27 | 28 | assert.Equal(t, expected, output) 29 | } 30 | 31 | func TestGetLoginEmpty(t *testing.T) { 32 | fs := afero.NewMemMapFs() 33 | cfg := config.Config{} 34 | 35 | file := filepath.Join(cfg.GetConfigFolder(os.Getenv("XDG_CONFIG")), "config.toml") 36 | 37 | afero.WriteFile(fs, file, []byte{}, os.ModePerm) 38 | 39 | output := getLogin(&fs, &cfg) 40 | 41 | assert.Equal(t, "", output) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/stripe/stripe-cli/pkg/validators" 9 | "github.com/stripe/stripe-cli/pkg/version" 10 | ) 11 | 12 | type versionCmd struct { 13 | cmd *cobra.Command 14 | } 15 | 16 | func newVersionCmd() *versionCmd { 17 | return &versionCmd{ 18 | cmd: &cobra.Command{ 19 | Use: "version", 20 | Args: validators.NoArgs, 21 | Short: "Get the version of the Stripe CLI", 22 | Run: func(cmd *cobra.Command, args []string) { 23 | fmt.Print(version.Template) 24 | 25 | version.CheckLatestVersion() 26 | }, 27 | }, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/config/config_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/spf13/viper" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestRemoveKey(t *testing.T) { 11 | v := viper.New() 12 | v.Set("remove", "me") 13 | v.Set("stay", "here") 14 | 15 | nv, err := removeKey(v, "remove") 16 | require.NoError(t, err) 17 | 18 | require.EqualValues(t, []string{"stay"}, nv.AllKeys()) 19 | require.ElementsMatch(t, []string{"stay", "remove"}, v.AllKeys()) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/account.application.deauthorized.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "account", 8 | "path": "/v1/accounts", 9 | "method": "post", 10 | "params": { 11 | "type": "standard" 12 | } 13 | }, 14 | { 15 | "name": "delete_account", 16 | "path": "/v1/accounts/${account:id}", 17 | "method": "delete", 18 | "params": {} 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/account.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "account", 8 | "path": "/v1/accounts", 9 | "method": "post", 10 | "params": { 11 | "type": "standard" 12 | } 13 | }, 14 | { 15 | "name": "update_account", 16 | "path": "/v1/accounts/${account:id}", 17 | "method": "post", 18 | "params": { 19 | "metadata": { 20 | "foo": "bar" 21 | } 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/balance.available.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 2000, 12 | "confirm": "true", 13 | "currency": "usd", 14 | "description": "(created by Stripe CLI)", 15 | "payment_method": "pm_card_bypassPending", 16 | "payment_method_types": ["card"], 17 | "shipping": { 18 | "name": "Jenny Rosen", 19 | "address": { 20 | "line1": "510 Townsend St", 21 | "postal_code": "94103", 22 | "city": "San Francisco", 23 | "state": "CA", 24 | "country": "US" 25 | } 26 | } 27 | } 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/billing_portal.configuration.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "billing_portal", 8 | "path": "/v1/billing_portal/configurations", 9 | "method": "post", 10 | "params": { 11 | "business_profile": { 12 | "headline": "created by the Stripe CLI", 13 | "privacy_policy_url": "https://www.example.com", 14 | "terms_of_service_url": "https://www.example.com" 15 | }, 16 | "features": { 17 | "subscription_cancel": { 18 | "enabled": true 19 | } 20 | } 21 | } 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/billing_portal.configuration.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "billing_portal", 8 | "path": "/v1/billing_portal/configurations", 9 | "method": "post", 10 | "params": { 11 | "business_profile": { 12 | "headline": "created by the Stripe CLI", 13 | "privacy_policy_url": "https://www.example.com", 14 | "terms_of_service_url": "https://www.example.com" 15 | }, 16 | "features": { 17 | "subscription_cancel": { 18 | "enabled": true 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "name": "billing_portal_updated", 25 | "path": "/v1/billing_portal/configurations/${billing_portal:id}", 26 | "method": "post", 27 | "params": { 28 | "metadata": { 29 | "foo": "bar" 30 | } 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/billing_portal.session.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "billing_portal", 16 | "path": "/v1/billing_portal/sessions", 17 | "method": "post", 18 | "params": { 19 | "customer": "${customer:id}" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/cash_balance.funds_available.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "fund_cash_balance", 16 | "path": "/v1/test_helpers/customers/${customer:id}/fund_cash_balance", 17 | "method": "post", 18 | "params": { 19 | "amount": 1000, 20 | "currency": "usd" 21 | } 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/charge.captured.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 2000, 12 | "capture_method": "manual", 13 | "confirm": true, 14 | "currency": "usd", 15 | "description": "(created by Stripe CLI)", 16 | "payment_method": "pm_card_visa", 17 | "payment_method_types": ["card"] 18 | } 19 | }, 20 | { 21 | "name": "capture", 22 | "path": "/v1/payment_intents/${payment_intent:id}/capture", 23 | "method": "post" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/charge.dispute.closed.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 100, 12 | "currency": "usd", 13 | "payment_method_types": ["card"], 14 | "payment_method": "pm_card_createDisputeInquiry", 15 | "confirm": true, 16 | "description": "(created by Stripe CLI)" 17 | } 18 | }, 19 | { 20 | "name": "disputes_list", 21 | "path": "/v1/disputes", 22 | "method": "get", 23 | "params": { 24 | "payment_intent": "${payment_intent:id}" 25 | } 26 | }, 27 | { 28 | "name": "disputes_close", 29 | "path": "/v1/disputes/${disputes_list:data.0.id}/close", 30 | "method": "post" 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/charge.dispute.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 100, 12 | "currency": "usd", 13 | "payment_method_types": ["card"], 14 | "payment_method": "pm_card_createDisputeInquiry", 15 | "confirm": true, 16 | "description": "(created by Stripe CLI)" 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/charge.dispute.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 100, 12 | "currency": "usd", 13 | "payment_method_types": ["card"], 14 | "payment_method": "pm_card_createDisputeInquiry", 15 | "confirm": true, 16 | "description": "(created by Stripe CLI)" 17 | } 18 | }, 19 | { 20 | "name": "disputes_list", 21 | "path": "/v1/disputes", 22 | "method": "get", 23 | "params": { 24 | "payment_intent": "${payment_intent:id}" 25 | } 26 | }, 27 | { 28 | "name": "disputes_update", 29 | "path": "/v1/disputes/${disputes_list:data.0.id}", 30 | "method": "post", 31 | "params": { 32 | "metadata": { 33 | "foo": "bar" 34 | } 35 | } 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/charge.failed.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "expected_error_type": "card_error", 9 | "path": "/v1/payment_intents", 10 | "method": "post", 11 | "params": { 12 | "amount": 100, 13 | "currency": "usd", 14 | "payment_method_types": ["card"], 15 | "payment_method": "pm_card_visa_chargeDeclined", 16 | "confirm": true, 17 | "description": "(created by Stripe CLI)" 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/charge.refund.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 100, 12 | "currency": "usd", 13 | "payment_method_types": ["card"], 14 | "payment_method": "pm_card_visa", 15 | "confirm": true, 16 | "description": "(created by Stripe CLI)" 17 | } 18 | }, 19 | { 20 | "name": "refund", 21 | "path": "/v1/refunds", 22 | "method": "post", 23 | "params": { 24 | "payment_intent": "${payment_intent:id}" 25 | } 26 | }, 27 | { 28 | "name": "updated_refund", 29 | "path": "/v1/refunds/${refund:id}", 30 | "method": "post", 31 | "params": { 32 | "metadata": {"order_id": "6735"} 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/charge.refunded.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 100, 12 | "currency": "usd", 13 | "payment_method_types": ["card"], 14 | "payment_method": "pm_card_visa", 15 | "confirm": true, 16 | "description": "(created by Stripe CLI)" 17 | } 18 | }, 19 | { 20 | "name": "refund", 21 | "path": "/v1/refunds", 22 | "method": "post", 23 | "params": { 24 | "payment_intent": "${payment_intent:id}" 25 | } 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/charge.succeeded.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 100, 12 | "currency": "usd", 13 | "payment_method_types": ["card"], 14 | "payment_method": "pm_card_visa", 15 | "confirm": true, 16 | "description": "(created by Stripe CLI)" 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/checkout.session.expired.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "product", 8 | "path": "/v1/products", 9 | "method": "post", 10 | "params": { 11 | "name": "myproduct", 12 | "description": "(created by Stripe CLI)" 13 | } 14 | }, 15 | { 16 | "name": "price", 17 | "path": "/v1/prices", 18 | "method": "post", 19 | "params": { 20 | "product": "${product:id}", 21 | "unit_amount": "1500", 22 | "currency": "usd" 23 | } 24 | }, 25 | { 26 | "name": "checkout_session", 27 | "path": "/v1/checkout/sessions", 28 | "method": "post", 29 | "params": { 30 | "success_url": "https://httpbin.org/post", 31 | "cancel_url": "https://httpbin.org/post", 32 | "mode": "payment", 33 | "line_items": [ 34 | { 35 | "price": "${price:id}", 36 | "quantity": 2 37 | } 38 | ] 39 | } 40 | }, 41 | { 42 | "name": "checkout_session_expired", 43 | "path": "/v1/checkout/sessions/${checkout_session:id}/expire", 44 | "method": "post" 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/coupon.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "coupon", 8 | "path": "/v1/coupons", 9 | "method": "post", 10 | "params": { 11 | "percent_off": 20 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/coupon.deleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "coupon", 8 | "path": "/v1/coupons", 9 | "method": "post", 10 | "params": { 11 | "percent_off": 20 12 | } 13 | },{ 14 | "name": "coupon_deleted", 15 | "path": "/v1/coupons/${coupon:id}", 16 | "method": "delete" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/coupon.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "coupon", 8 | "path": "/v1/coupons", 9 | "method": "post", 10 | "params": { 11 | "percent_off": 20 12 | } 13 | },{ 14 | "name": "coupon_updated", 15 | "path": "/v1/coupons/${coupon:id}", 16 | "method": "post", 17 | "params": { 18 | "metadata": { 19 | "foo": "bar" 20 | } 21 | } 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/credit_note.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | }, 25 | { 26 | "name": "invoice", 27 | "path": "/v1/invoices", 28 | "method": "post", 29 | "params": { 30 | "customer": "${customer:id}", 31 | "description": "(created by Stripe CLI)", 32 | "pending_invoice_items_behavior": "include" 33 | } 34 | }, 35 | { 36 | "name": "invoice_finalized", 37 | "path": "/v1/invoices/${invoice:id}/finalize", 38 | "method": "post" 39 | }, 40 | { 41 | "name": "credit_note", 42 | "path": "/v1/credit_notes", 43 | "method": "post", 44 | "params": { 45 | "invoice": "${invoice:id}", 46 | "lines": [{ 47 | "type": "custom_line_item", 48 | "unit_amount": 1000, 49 | "quantity": 1, 50 | "description": "(created by Stripe CLI)" 51 | }] 52 | } 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/credit_note.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | }, 25 | { 26 | "name": "invoice", 27 | "path": "/v1/invoices", 28 | "method": "post", 29 | "params": { 30 | "customer": "${customer:id}", 31 | "description": "(created by Stripe CLI)", 32 | "pending_invoice_items_behavior": "include" 33 | } 34 | }, 35 | { 36 | "name": "invoice_finalized", 37 | "path": "/v1/invoices/${invoice:id}/finalize", 38 | "method": "post" 39 | }, 40 | { 41 | "name": "credit_note", 42 | "path": "/v1/credit_notes", 43 | "method": "post", 44 | "params": { 45 | "invoice": "${invoice:id}", 46 | "lines": [{ 47 | "type": "custom_line_item", 48 | "unit_amount": 1000, 49 | "quantity": 1, 50 | "description": "(created by Stripe CLI)" 51 | }] 52 | } 53 | }, 54 | { 55 | "name": "credit_note_updated", 56 | "path": "/v1/credit_notes/${credit_note:id}", 57 | "method": "post", 58 | "params": { 59 | "metadata": { 60 | "foo": "bar" 61 | } 62 | } 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/credit_note.voided.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | }, 25 | { 26 | "name": "invoice", 27 | "path": "/v1/invoices", 28 | "method": "post", 29 | "params": { 30 | "customer": "${customer:id}", 31 | "description": "(created by Stripe CLI)", 32 | "pending_invoice_items_behavior": "include" 33 | } 34 | }, 35 | { 36 | "name": "invoice_finalized", 37 | "path": "/v1/invoices/${invoice:id}/finalize", 38 | "method": "post" 39 | }, 40 | { 41 | "name": "credit_note", 42 | "path": "/v1/credit_notes", 43 | "method": "post", 44 | "params": { 45 | "invoice": "${invoice:id}", 46 | "lines": [{ 47 | "type": "custom_line_item", 48 | "unit_amount": 1000, 49 | "quantity": 1, 50 | "description": "(created by Stripe CLI)" 51 | }] 52 | } 53 | }, 54 | { 55 | "name": "credit_note_voided", 56 | "path": "/v1/credit_notes/${credit_note:id}/void", 57 | "method": "post" 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.deleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "customer_deleted", 16 | "path": "/v1/customers/${customer:id}", 17 | "method": "delete" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.discount.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "coupon", 8 | "path": "/v1/coupons", 9 | "method": "post", 10 | "params": { 11 | "percent_off": 20 12 | } 13 | }, 14 | { 15 | "name": "customer", 16 | "path": "/v1/customers", 17 | "method": "post", 18 | "params": { 19 | "coupon": "${coupon:id}", 20 | "description": "(created by Stripe CLI)" 21 | } 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.discount.deleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "coupon", 8 | "path": "/v1/coupons", 9 | "method": "post", 10 | "params": { 11 | "percent_off": 20 12 | } 13 | }, 14 | { 15 | "name": "customer", 16 | "path": "/v1/customers", 17 | "method": "post", 18 | "params": { 19 | "coupon": "${coupon:id}", 20 | "description": "(created by Stripe CLI)" 21 | } 22 | }, 23 | { 24 | "name": "customer_updated", 25 | "path": "/v1/customers/${customer:id}", 26 | "method": "post", 27 | "params": { 28 | "coupon": "" 29 | } 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.discount.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "coupon_1", 8 | "path": "/v1/coupons", 9 | "method": "post", 10 | "params": { 11 | "percent_off": 20 12 | } 13 | }, 14 | { 15 | "name": "customer", 16 | "path": "/v1/customers", 17 | "method": "post", 18 | "params": { 19 | "coupon": "${coupon_1:id}", 20 | "description": "(created by Stripe CLI)" 21 | } 22 | }, 23 | { 24 | "name": "coupon_2", 25 | "path": "/v1/coupons", 26 | "method": "post", 27 | "params": { 28 | "percent_off": 30 29 | } 30 | }, 31 | { 32 | "name": "customer_updated", 33 | "path": "/v1/customers/${customer:id}", 34 | "method": "post", 35 | "params": { 36 | "coupon": "${coupon_2:id}" 37 | } 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.source.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "customer_source", 16 | "path": "/v1/customers/${customer:id}/sources", 17 | "method": "post", 18 | "params": { 19 | "source": "tok_visa" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.source.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "customer_source", 16 | "path": "/v1/customers/${customer:id}/sources", 17 | "method": "post", 18 | "params": { 19 | "source": "tok_visa" 20 | } 21 | }, 22 | { 23 | "name": "customer_source_updated", 24 | "path": "/v1/customers/${customer:id}/sources/${customer_source:id}", 25 | "method": "post", 26 | "params": { 27 | "metadata": { 28 | "foo": "bar" 29 | } 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.subscription.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "payment_method": "pm_card_visa", 13 | "invoice_settings": { 14 | "default_payment_method": "pm_card_visa" 15 | } 16 | } 17 | }, 18 | { 19 | "name": "product", 20 | "path": "/v1/products", 21 | "method": "post", 22 | "params": { 23 | "name": "myproduct", 24 | "description": "(created by Stripe CLI)" 25 | } 26 | }, 27 | { 28 | "name": "price", 29 | "path": "/v1/prices", 30 | "method": "post", 31 | "params": { 32 | "product": "${product:id}", 33 | "unit_amount": "1500", 34 | "currency": "usd", 35 | "recurring[interval]": "month" 36 | } 37 | }, 38 | { 39 | "name": "subscription", 40 | "path": "/v1/subscriptions", 41 | "method": "post", 42 | "params": { 43 | "customer": "${customer:id}", 44 | "items": [ 45 | { 46 | "price": "${price:id}" 47 | } 48 | ] 49 | } 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.subscription.deleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "payment_method": "pm_card_visa", 13 | "invoice_settings": { 14 | "default_payment_method": "pm_card_visa" 15 | } 16 | } 17 | }, 18 | { 19 | "name": "product", 20 | "path": "/v1/products", 21 | "method": "post", 22 | "params": { 23 | "name": "myproduct", 24 | "description": "(created by Stripe CLI)" 25 | } 26 | }, 27 | { 28 | "name": "price", 29 | "path": "/v1/prices", 30 | "method": "post", 31 | "params": { 32 | "product": "${product:id}", 33 | "unit_amount": "1500", 34 | "currency": "usd", 35 | "recurring[interval]": "month" 36 | } 37 | }, 38 | { 39 | "name": "subscription", 40 | "path": "/v1/subscriptions", 41 | "method": "post", 42 | "params": { 43 | "customer": "${customer:id}", 44 | "items": [ 45 | { 46 | "price": "${price:id}" 47 | } 48 | ] 49 | } 50 | }, 51 | { 52 | "name": "subscription_deleted", 53 | "path": "/v1/subscriptions/${subscription:id}", 54 | "method": "delete" 55 | } 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.subscription.paused.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "product", 16 | "path": "/v1/products", 17 | "method": "post", 18 | "params": { 19 | "name": "myproduct", 20 | "description": "(created by Stripe CLI)" 21 | } 22 | }, 23 | { 24 | "name": "price", 25 | "path": "/v1/prices", 26 | "method": "post", 27 | "params": { 28 | "product": "${product:id}", 29 | "unit_amount": "1500", 30 | "currency": "usd", 31 | "recurring[interval]": "month" 32 | } 33 | }, 34 | { 35 | "name": "subscription", 36 | "path": "/v1/subscriptions", 37 | "method": "post", 38 | "params": { 39 | "customer": "${customer:id}", 40 | "items": [ 41 | { 42 | "price": "${price:id}" 43 | } 44 | ], 45 | "trial_period_days": 7, 46 | "trial_settings": { 47 | "end_behavior": { 48 | "missing_payment_method": "pause" 49 | } 50 | } 51 | } 52 | }, 53 | { 54 | "name": "subscription_pause", 55 | "path": "/v1/subscriptions/${subscription:id}", 56 | "method": "post", 57 | "params": { 58 | "trial_end": "now" 59 | } 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.subscription.trial_will_end.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "product", 16 | "path": "/v1/products", 17 | "method": "post", 18 | "params": { 19 | "name": "myproduct", 20 | "description": "(created by Stripe CLI)" 21 | } 22 | }, 23 | { 24 | "name": "price", 25 | "path": "/v1/prices", 26 | "method": "post", 27 | "params": { 28 | "product": "${product:id}", 29 | "unit_amount": "1500", 30 | "currency": "usd", 31 | "recurring[interval]": "month" 32 | } 33 | }, 34 | { 35 | "name": "subscription", 36 | "path": "/v1/subscriptions", 37 | "method": "post", 38 | "params": { 39 | "customer": "${customer:id}", 40 | "items": [ 41 | { 42 | "price": "${price:id}" 43 | } 44 | ], 45 | "trial_period_days": 1 46 | } 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.subscription.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "payment_method": "pm_card_visa", 13 | "invoice_settings": { 14 | "default_payment_method": "pm_card_visa" 15 | } 16 | } 17 | }, 18 | { 19 | "name": "product", 20 | "path": "/v1/products", 21 | "method": "post", 22 | "params": { 23 | "name": "myproduct", 24 | "description": "(created by Stripe CLI)" 25 | } 26 | }, 27 | { 28 | "name": "price", 29 | "path": "/v1/prices", 30 | "method": "post", 31 | "params": { 32 | "product": "${product:id}", 33 | "unit_amount": "1500", 34 | "currency": "usd", 35 | "recurring[interval]": "month" 36 | } 37 | }, 38 | { 39 | "name": "subscription", 40 | "path": "/v1/subscriptions", 41 | "method": "post", 42 | "params": { 43 | "customer": "${customer:id}", 44 | "items": [ 45 | { 46 | "price": "${price:id}" 47 | } 48 | ] 49 | } 50 | }, 51 | { 52 | "name": "subscription_updated", 53 | "path": "/v1/subscriptions/${subscription:id}", 54 | "method": "post", 55 | "params": { 56 | "metadata": { 57 | "foo": "bar" 58 | } 59 | } 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "customer_updated", 16 | "path": "/v1/customers/${customer:id}", 17 | "method": "post", 18 | "params": { 19 | "metadata": { 20 | "foo": "bar" 21 | } 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/customer_cash_balance_transaction.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "fund_cash_balance", 16 | "path": "/v1/test_helpers/customers/${customer:id}/fund_cash_balance", 17 | "method": "post", 18 | "params": { 19 | "amount": 1000, 20 | "currency": "usd" 21 | } 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/identity.verification_session.canceled.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "verification_session", 8 | "path": "/v1/identity/verification_sessions", 9 | "method": "post", 10 | "params": { 11 | "type": "document", 12 | "options[document][require_matching_selfie]": true, 13 | "return_url": "https://stripe.com" 14 | } 15 | }, 16 | { 17 | "name": "verification_session_canceled", 18 | "path": "/v1/identity/verification_sessions/${verification_session:id}/cancel", 19 | "method": "post" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/identity.verification_session.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "verification_session", 8 | "path": "/v1/identity/verification_sessions", 9 | "method": "post", 10 | "params": { 11 | "type": "document", 12 | "options[document][require_matching_selfie]": true, 13 | "return_url": "https://stripe.com" 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/identity.verification_session.redacted.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "verification_session", 8 | "path": "/v1/identity/verification_sessions", 9 | "method": "post", 10 | "params": { 11 | "type": "document", 12 | "options[document][require_matching_selfie]": true, 13 | "return_url": "https://stripe.com" 14 | } 15 | }, 16 | { 17 | "name": "verification_session_redacted", 18 | "path": "/v1/identity/verification_sessions/${verification_session:id}/redact", 19 | "method": "post" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | }, 25 | { 26 | "name": "invoice", 27 | "path": "/v1/invoices", 28 | "method": "post", 29 | "params": { 30 | "customer": "${customer:id}", 31 | "description": "(created by Stripe CLI)", 32 | "pending_invoice_items_behavior": "include" 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.deleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | }, 25 | { 26 | "name": "invoice", 27 | "path": "/v1/invoices", 28 | "method": "post", 29 | "params": { 30 | "customer": "${customer:id}", 31 | "description": "(created by Stripe CLI)", 32 | "pending_invoice_items_behavior": "include" 33 | } 34 | }, 35 | { 36 | "name": "invoice_delete", 37 | "path": "/v1/invoices/${invoice:id}", 38 | "method": "delete" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.finalized.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | }, 25 | { 26 | "name": "invoice", 27 | "path": "/v1/invoices", 28 | "method": "post", 29 | "params": { 30 | "customer": "${customer:id}", 31 | "description": "(created by Stripe CLI)", 32 | "pending_invoice_items_behavior": "include" 33 | } 34 | }, 35 | { 36 | "name": "invoice_finalized", 37 | "path": "/v1/invoices/${invoice:id}/finalize", 38 | "method": "post" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.marked_uncollectible.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | }, 25 | { 26 | "name": "invoice", 27 | "path": "/v1/invoices", 28 | "method": "post", 29 | "params": { 30 | "customer": "${customer:id}", 31 | "description": "(created by Stripe CLI)", 32 | "pending_invoice_items_behavior": "include" 33 | } 34 | }, 35 | { 36 | "name": "invoice_finalize", 37 | "path": "/v1/invoices/${invoice:id}/finalize", 38 | "method": "post" 39 | }, 40 | { 41 | "name": "invoice_mark_uncollectible", 42 | "path": "/v1/invoices/${invoice:id}/mark_uncollectible", 43 | "method": "post" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.paid.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "payment_method", 16 | "path": "/v1/payment_methods/pm_card_visa/attach", 17 | "method": "post", 18 | "params": { 19 | "customer": "${customer:id}" 20 | } 21 | }, 22 | { 23 | "name": "invoiceitem", 24 | "path": "/v1/invoiceitems", 25 | "method": "post", 26 | "params": { 27 | "amount": 2000, 28 | "currency": "usd", 29 | "customer": "${customer:id}", 30 | "description": "(created by Stripe CLI)" 31 | } 32 | }, 33 | { 34 | "name": "invoice", 35 | "path": "/v1/invoices", 36 | "method": "post", 37 | "params": { 38 | "customer": "${customer:id}", 39 | "description": "(created by Stripe CLI)", 40 | "pending_invoice_items_behavior": "include" 41 | } 42 | }, 43 | { 44 | "name": "invoice_pay", 45 | "path": "/v1/invoices/${invoice:id}/pay", 46 | "method": "post", 47 | "params": { 48 | "payment_method": "${payment_method:id}" 49 | } 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.payment_action_required.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "payment_method", 16 | "path": "/v1/payment_methods/pm_card_threeDSecure2Required/attach", 17 | "method": "post", 18 | "params": { 19 | "customer": "${customer:id}" 20 | } 21 | }, 22 | { 23 | "name": "invoiceitem", 24 | "path": "/v1/invoiceitems", 25 | "method": "post", 26 | "params": { 27 | "amount": 2000, 28 | "currency": "usd", 29 | "customer": "${customer:id}", 30 | "description": "(created by Stripe CLI)" 31 | } 32 | }, 33 | { 34 | "name": "invoice", 35 | "path": "/v1/invoices", 36 | "method": "post", 37 | "params": { 38 | "customer": "${customer:id}", 39 | "description": "(created by Stripe CLI)", 40 | "pending_invoice_items_behavior": "include" 41 | } 42 | }, 43 | { 44 | "name": "pay_invoice", 45 | "path": "/v1/invoices/${invoice:id}/pay", 46 | "method": "post", 47 | "expected_error_type": "card_error", 48 | "params": { 49 | "payment_method": "${payment_method:id}" 50 | } 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.payment_failed.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "payment_method", 16 | "path": "/v1/payment_methods/pm_card_chargeCustomerFail/attach", 17 | "method": "post", 18 | "params": { 19 | "customer": "${customer:id}" 20 | } 21 | }, 22 | { 23 | "name": "invoiceitem", 24 | "path": "/v1/invoiceitems", 25 | "method": "post", 26 | "params": { 27 | "amount": 2000, 28 | "currency": "usd", 29 | "customer": "${customer:id}", 30 | "description": "(created by Stripe CLI)" 31 | } 32 | }, 33 | { 34 | "name": "invoice", 35 | "path": "/v1/invoices", 36 | "method": "post", 37 | "params": { 38 | "customer": "${customer:id}", 39 | "description": "(created by Stripe CLI)", 40 | "pending_invoice_items_behavior": "include" 41 | } 42 | }, 43 | { 44 | "name": "invoice_pay", 45 | "expected_error_type": "card_error", 46 | "path": "/v1/invoices/${invoice:id}/pay", 47 | "method": "post", 48 | "params": { 49 | "payment_method": "${payment_method:id}" 50 | } 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.payment_succeeded.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "payment_method", 16 | "path": "/v1/payment_methods/pm_card_visa/attach", 17 | "method": "post", 18 | "params": { 19 | "customer": "${customer:id}" 20 | } 21 | }, 22 | { 23 | "name": "invoiceitem", 24 | "path": "/v1/invoiceitems", 25 | "method": "post", 26 | "params": { 27 | "amount": 2000, 28 | "currency": "usd", 29 | "customer": "${customer:id}", 30 | "description": "(created by Stripe CLI)" 31 | } 32 | }, 33 | { 34 | "name": "invoice", 35 | "path": "/v1/invoices", 36 | "method": "post", 37 | "params": { 38 | "customer": "${customer:id}", 39 | "description": "(created by Stripe CLI)", 40 | "pending_invoice_items_behavior": "include" 41 | } 42 | }, 43 | { 44 | "name": "invoice_pay", 45 | "path": "/v1/invoices/${invoice:id}/pay", 46 | "method": "post", 47 | "params": { 48 | "payment_method": "${payment_method:id}" 49 | } 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.sent.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "email": "bob@example.com", 12 | "description": "(created by Stripe CLI)" 13 | } 14 | }, 15 | { 16 | "name": "invoiceitem", 17 | "path": "/v1/invoiceitems", 18 | "method": "post", 19 | "params": { 20 | "amount": 2000, 21 | "currency": "usd", 22 | "customer": "${customer:id}", 23 | "description": "(created by Stripe CLI)" 24 | } 25 | }, 26 | { 27 | "name": "invoice", 28 | "path": "/v1/invoices", 29 | "method": "post", 30 | "params": { 31 | "customer": "${customer:id}", 32 | "description": "(created by Stripe CLI)", 33 | "pending_invoice_items_behavior": "include", 34 | "collection_method": "send_invoice", 35 | "days_until_due": 7 36 | } 37 | }, 38 | { 39 | "name": "invoice_send", 40 | "path": "/v1/invoices/${invoice:id}/send", 41 | "method": "post" 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | }, 25 | { 26 | "name": "invoice", 27 | "path": "/v1/invoices", 28 | "method": "post", 29 | "params": { 30 | "customer": "${customer:id}", 31 | "description": "(created by Stripe CLI)", 32 | "pending_invoice_items_behavior": "include" 33 | } 34 | }, 35 | { 36 | "name": "invoice_updated", 37 | "path": "/v1/invoices/${invoice:id}", 38 | "method": "post", 39 | "params": { 40 | "metadata": { 41 | "foo": "bar" 42 | } 43 | } 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoice.voided.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | }, 25 | { 26 | "name": "invoice", 27 | "path": "/v1/invoices", 28 | "method": "post", 29 | "params": { 30 | "customer": "${customer:id}", 31 | "description": "(created by Stripe CLI)", 32 | "pending_invoice_items_behavior": "include" 33 | } 34 | }, 35 | { 36 | "name": "invoice_finalize", 37 | "path": "/v1/invoices/${invoice:id}/finalize", 38 | "method": "post" 39 | }, 40 | { 41 | "name": "invoice_void", 42 | "path": "/v1/invoices/${invoice:id}/void", 43 | "method": "post" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoiceitem.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/invoiceitem.deleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "invoiceitem", 16 | "path": "/v1/invoiceitems", 17 | "method": "post", 18 | "params": { 19 | "amount": 2000, 20 | "currency": "usd", 21 | "customer": "${customer:id}", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | } 25 | , 26 | { 27 | "name": "invoiceitem_delete", 28 | "path": "/v1/invoiceitems/${invoiceitem:id}", 29 | "method": "delete" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/issuing_authorization.request.eu.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "cardholder", 8 | "path": "/v1/issuing/cardholders", 9 | "method": "post", 10 | "params": { 11 | "name": "My Cardholder", 12 | "type": "individual", 13 | "phone_number": "+34666777888", 14 | "individual": { 15 | "first_name": "My", 16 | "last_name": "Cardholder", 17 | "card_issuing": { 18 | "user_terms_acceptance": { 19 | "date": 1470266163, 20 | "ip": "91.121.146.224" 21 | } 22 | } 23 | }, 24 | "billing": { 25 | "address": { 26 | "line1": "P. de la Castellana 43", 27 | "city": "Madrid", 28 | "state": "Madrid", 29 | "postal_code": "28046", 30 | "country": "ES" 31 | } 32 | } 33 | } 34 | }, 35 | { 36 | "name": "card", 37 | "path": "/v1/issuing/cards", 38 | "method": "post", 39 | "params": { 40 | "currency": "eur", 41 | "type": "virtual", 42 | "cardholder": "${cardholder:id}", 43 | "status": "active" 44 | } 45 | }, 46 | { 47 | "name": "authorization_request", 48 | "path": "/v1/issuing/cards/${card:id}/test/authorizations", 49 | "method": "post", 50 | "params": { 51 | "held_amount": 100, 52 | "held_currency": "eur", 53 | "authorization_method": "online" 54 | } 55 | } 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/issuing_authorization.request.gb.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "cardholder", 8 | "path": "/v1/issuing/cardholders", 9 | "method": "post", 10 | "params": { 11 | "name": "My Cardholder", 12 | "type": "individual", 13 | "phone_number": "+447700900000", 14 | "individual": { 15 | "first_name": "My", 16 | "last_name": "Cardholder", 17 | "card_issuing": { 18 | "user_terms_acceptance": { 19 | "date": 1470266163, 20 | "ip": "91.121.146.224" 21 | } 22 | } 23 | }, 24 | "billing": { 25 | "address": { 26 | "line1": "211 Old Street", 27 | "city": "London", 28 | "state": "London", 29 | "postal_code": "EC1V 9NR", 30 | "country": "GB" 31 | } 32 | } 33 | } 34 | }, 35 | { 36 | "name": "card", 37 | "path": "/v1/issuing/cards", 38 | "method": "post", 39 | "params": { 40 | "currency": "gbp", 41 | "type": "virtual", 42 | "cardholder": "${cardholder:id}", 43 | "status": "active" 44 | } 45 | }, 46 | { 47 | "name": "authorization_request", 48 | "path": "/v1/issuing/cards/${card:id}/test/authorizations", 49 | "method": "post", 50 | "params": { 51 | "held_amount": 100, 52 | "held_currency": "gbp", 53 | "authorization_method": "online" 54 | } 55 | } 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/issuing_authorization.request.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "cardholder", 8 | "path": "/v1/issuing/cardholders", 9 | "method": "post", 10 | "params": { 11 | "name": "My Cardholder", 12 | "type": "individual", 13 | "individual": { 14 | "first_name": "My", 15 | "last_name": "Cardholder", 16 | "card_issuing": { 17 | "user_terms_acceptance": { 18 | "date": 1470266163, 19 | "ip": "91.121.146.224" 20 | } 21 | } 22 | }, 23 | "billing": { 24 | "address": { 25 | "line1": "1234 Main Street", 26 | "city": "San Francisco", 27 | "state": "CA", 28 | "postal_code": "94111", 29 | "country": "US" 30 | } 31 | } 32 | } 33 | }, 34 | { 35 | "name": "card", 36 | "path": "/v1/issuing/cards", 37 | "method": "post", 38 | "params": { 39 | "currency": "usd", 40 | "type": "virtual", 41 | "cardholder": "${cardholder:id}", 42 | "status": "active" 43 | } 44 | }, 45 | { 46 | "name": "authorization_request", 47 | "path": "/v1/issuing/cards/${card:id}/test/authorizations", 48 | "method": "post", 49 | "params": { 50 | "held_amount": 100, 51 | "authorization_method": "online" 52 | } 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/issuing_card.created.eu.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "cardholder", 8 | "path": "/v1/issuing/cardholders", 9 | "method": "post", 10 | "params": { 11 | "name": "My Cardholder", 12 | "type": "individual", 13 | "phone_number": "+34666777888", 14 | "individual": { 15 | "first_name": "My", 16 | "last_name": "Cardholder", 17 | "card_issuing": { 18 | "user_terms_acceptance": { 19 | "date": 1470266163, 20 | "ip": "91.121.146.224" 21 | } 22 | } 23 | }, 24 | "billing": { 25 | "address": { 26 | "line1": "P. de la Castellana 43", 27 | "city": "Madrid", 28 | "state": "Madrid", 29 | "postal_code": "28046", 30 | "country": "ES" 31 | } 32 | } 33 | } 34 | }, 35 | { 36 | "name": "card", 37 | "path": "/v1/issuing/cards", 38 | "method": "post", 39 | "params": { 40 | "currency": "eur", 41 | "type": "virtual", 42 | "cardholder": "${cardholder:id}" 43 | } 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/issuing_card.created.gb.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "cardholder", 8 | "path": "/v1/issuing/cardholders", 9 | "method": "post", 10 | "params": { 11 | "name": "My Cardholder", 12 | "type": "individual", 13 | "phone_number": "+447700900000", 14 | "individual": { 15 | "first_name": "My", 16 | "last_name": "Cardholder", 17 | "card_issuing": { 18 | "user_terms_acceptance": { 19 | "date": 1470266163, 20 | "ip": "91.121.146.224" 21 | } 22 | } 23 | }, 24 | "billing": { 25 | "address": { 26 | "line1": "211 Old Street", 27 | "city": "London", 28 | "state": "London", 29 | "postal_code": "EC1V 9NR", 30 | "country": "GB" 31 | } 32 | } 33 | } 34 | }, 35 | { 36 | "name": "card", 37 | "path": "/v1/issuing/cards", 38 | "method": "post", 39 | "params": { 40 | "currency": "gbp", 41 | "type": "virtual", 42 | "cardholder": "${cardholder:id}" 43 | } 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/issuing_card.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "cardholder", 8 | "path": "/v1/issuing/cardholders", 9 | "method": "post", 10 | "params": { 11 | "name": "My Cardholder", 12 | "type": "individual", 13 | "individual": { 14 | "first_name": "My", 15 | "last_name": "Cardholder", 16 | "card_issuing": { 17 | "user_terms_acceptance": { 18 | "date": 1470266163, 19 | "ip": "91.121.146.224" 20 | } 21 | } 22 | }, 23 | "billing": { 24 | "address": { 25 | "line1": "1234 Main Street", 26 | "city": "San Francisco", 27 | "state": "CA", 28 | "postal_code": "94111", 29 | "country": "US" 30 | } 31 | } 32 | } 33 | }, 34 | { 35 | "name": "card", 36 | "path": "/v1/issuing/cards", 37 | "method": "post", 38 | "params": { 39 | "currency": "usd", 40 | "type": "virtual", 41 | "cardholder": "${cardholder:id}" 42 | } 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/issuing_cardholder.created.eu.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "cardholder", 8 | "path": "/v1/issuing/cardholders", 9 | "method": "post", 10 | "params": { 11 | "name": "My Cardholder", 12 | "type": "individual", 13 | "phone_number": "+34666777888", 14 | "individual": { 15 | "first_name": "My", 16 | "last_name": "Cardholder", 17 | "card_issuing": { 18 | "user_terms_acceptance": { 19 | "date": 1470266163, 20 | "ip": "91.121.146.224" 21 | } 22 | } 23 | }, 24 | "billing": { 25 | "address": { 26 | "line1": "P. de la Castellana 43", 27 | "city": "Madrid", 28 | "state": "Madrid", 29 | "postal_code": "28046", 30 | "country": "ES" 31 | } 32 | } 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/issuing_cardholder.created.gb.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "cardholder", 8 | "path": "/v1/issuing/cardholders", 9 | "method": "post", 10 | "params": { 11 | "name": "My Cardholder", 12 | "type": "individual", 13 | "phone_number": "+447700900000", 14 | "individual": { 15 | "first_name": "My", 16 | "last_name": "Cardholder", 17 | "card_issuing": { 18 | "user_terms_acceptance": { 19 | "date": 1470266163, 20 | "ip": "91.121.146.224" 21 | } 22 | } 23 | }, 24 | "billing": { 25 | "address": { 26 | "line1": "211 Old Street", 27 | "city": "London", 28 | "state": "London", 29 | "postal_code": "EC1V 9NR", 30 | "country": "GB" 31 | } 32 | } 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/issuing_cardholder.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "cardholder", 8 | "path": "/v1/issuing/cardholders", 9 | "method": "post", 10 | "params": { 11 | "name": "My Cardholder", 12 | "type": "individual", 13 | "individual": { 14 | "first_name": "My", 15 | "last_name": "Cardholder", 16 | "card_issuing": { 17 | "user_terms_acceptance": { 18 | "date": 1470266163, 19 | "ip": "91.121.146.224" 20 | } 21 | } 22 | }, 23 | "billing": { 24 | "address": { 25 | "line1": "1234 Main Street", 26 | "city": "San Francisco", 27 | "state": "CA", 28 | "postal_code": "94111", 29 | "country": "US" 30 | } 31 | } 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_intent.amount_capturable_updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 2000, 12 | "capture_method": "manual", 13 | "confirm": true, 14 | "confirmation_method": "manual", 15 | "currency": "usd", 16 | "description": "(created by Stripe CLI)", 17 | "payment_method_types": ["card"], 18 | "payment_method": "pm_card_visa", 19 | "return_url": "https://stripe.com" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_intent.canceled.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 2000, 12 | "currency": "usd", 13 | "description": "(created by Stripe CLI)" 14 | } 15 | }, 16 | { 17 | "name": "payment_intent_canceled", 18 | "path": "/v1/payment_intents/${payment_intent:id}/cancel", 19 | "method": "post", 20 | "params": { 21 | "cancellation_reason": "requested_by_customer" 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_intent.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 2000, 12 | "currency": "usd", 13 | "description": "(created by Stripe CLI)", 14 | "payment_method_types": ["card"] 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_intent.partially_funded.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "_meta": { 4 | "template_version": 0 5 | }, 6 | "fixtures": [ 7 | { 8 | "name": "customer", 9 | "path": "/v1/customers", 10 | "method": "post", 11 | "params": { 12 | "description": "(created by Stripe CLI)" 13 | } 14 | }, 15 | { 16 | "name": "payment_intent", 17 | "path": "/v1/payment_intents", 18 | "method": "post", 19 | "params": { 20 | "amount": 20000, 21 | "currency": "jpy", 22 | "customer": "${customer:id}", 23 | "description": "(created by Stripe CLI)", 24 | "payment_method_types[]": "customer_balance", 25 | "payment_method_data[type]": "customer_balance", 26 | "payment_method_options[customer_balance][funding_type]": "bank_transfer", 27 | "payment_method_options[customer_balance][bank_transfer][type]": "jp_bank_account", 28 | "confirm": "true" 29 | } 30 | }, 31 | { 32 | "name": "fund_cash_balance", 33 | "path": "/v1/test_helpers/customers/${customer:id}/fund_cash_balance", 34 | "method": "post", 35 | "params": { 36 | "amount": 10000, 37 | "currency": "jpy" 38 | } 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_intent.payment_failed.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "expected_error_type": "card_error", 9 | "path": "/v1/payment_intents", 10 | "method": "post", 11 | "params": { 12 | "amount": 2000, 13 | "confirm": "true", 14 | "currency": "usd", 15 | "description": "(created by Stripe CLI)", 16 | "payment_method": "pm_card_chargeDeclined", 17 | "payment_method_types": ["card"] 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_intent.processing.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_method", 8 | "path": "/v1/payment_methods", 9 | "method": "post", 10 | "params": { 11 | "type": "sepa_debit", 12 | "sepa_debit": { 13 | "iban": "AT321904300235473204" 14 | }, 15 | "billing_details": { 16 | "name": "Bob", 17 | "email": "bob@example.com" 18 | } 19 | } 20 | }, 21 | { 22 | "name": "payment_intent", 23 | "path": "/v1/payment_intents", 24 | "method": "post", 25 | "params": { 26 | "payment_method": "${payment_method:id}", 27 | "payment_method_types": [ "sepa_debit" ], 28 | "amount": 1000, 29 | "currency": "eur", 30 | "confirm": true, 31 | "mandate_data": { 32 | "customer_acceptance": { 33 | "accepted_at": "1590590577", 34 | "type": "offline" 35 | } 36 | } 37 | } 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_intent.requires_action.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "_meta": { 4 | "template_version": 0 5 | }, 6 | "fixtures": [ 7 | { 8 | "name": "payment_intent", 9 | "path": "/v1/payment_intents", 10 | "method": "post", 11 | "params": { 12 | "amount": 2000, 13 | "currency": "usd", 14 | "confirm": "true", 15 | "description": "(created by Stripe CLI)", 16 | "payment_method": "pm_card_authenticationRequired", 17 | "payment_method_types": ["card"] 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_intent.succeeded.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 2000, 12 | "confirm": "true", 13 | "currency": "usd", 14 | "description": "(created by Stripe CLI)", 15 | "payment_method": "pm_card_visa", 16 | "payment_method_types": ["card"], 17 | "shipping": { 18 | "name": "Jenny Rosen", 19 | "address": { 20 | "line1": "510 Townsend St", 21 | "postal_code": "94103", 22 | "city": "San Francisco", 23 | "state": "CA", 24 | "country": "US" 25 | } 26 | } 27 | } 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_link.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "product", 8 | "path": "/v1/products", 9 | "method": "post", 10 | "params": { 11 | "name": "myproduct", 12 | "description": "(created by Stripe CLI)" 13 | } 14 | }, 15 | { 16 | "name": "price", 17 | "path": "/v1/prices", 18 | "method": "post", 19 | "params": { 20 | "product": "${product:id}", 21 | "unit_amount": "1500", 22 | "currency": "usd", 23 | "recurring[interval]": "month" 24 | } 25 | }, 26 | { 27 | "name": "payment_link", 28 | "path": "/v1/payment_links", 29 | "method": "post", 30 | "params": { 31 | "line_items": { 32 | "0": { 33 | "price": "${price:id}", 34 | "quantity": 1 35 | } 36 | } 37 | } 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_link.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "product", 8 | "path": "/v1/products", 9 | "method": "post", 10 | "params": { 11 | "name": "myproduct", 12 | "description": "(created by Stripe CLI)" 13 | } 14 | }, 15 | { 16 | "name": "price", 17 | "path": "/v1/prices", 18 | "method": "post", 19 | "params": { 20 | "product": "${product:id}", 21 | "unit_amount": "1500", 22 | "currency": "usd", 23 | "recurring[interval]": "month" 24 | } 25 | }, 26 | { 27 | "name": "payment_link", 28 | "path": "/v1/payment_links", 29 | "method": "post", 30 | "params": { 31 | "line_items": { 32 | "0": { 33 | "price": "${price:id}", 34 | "quantity": 1 35 | } 36 | } 37 | } 38 | }, 39 | { 40 | "name": "payment_link_updated", 41 | "path": "/v1/payment_links/${payment_link:id}", 42 | "method": "post", 43 | "params": { 44 | "allow_promotion_codes": true 45 | } 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_method.attached.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "payment_method_attach", 16 | "path": "/v1/payment_methods/pm_card_visa/attach", 17 | "method": "post", 18 | "params": { 19 | "customer": "${customer:id}" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_method.detached.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "payment_method_attach", 16 | "path": "/v1/payment_methods/pm_card_visa/attach", 17 | "method": "post", 18 | "params": { 19 | "customer": "${customer:id}" 20 | } 21 | }, 22 | { 23 | "name": "payment_method_detach", 24 | "path": "/v1/payment_methods/${payment_method_attach:id}/detach", 25 | "method": "post" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payment_method.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)" 12 | } 13 | }, 14 | { 15 | "name": "setup_intent", 16 | "path": "/v1/setup_intents", 17 | "method": "post", 18 | "params": { 19 | "customer": "${customer:id}", 20 | "payment_method": "pm_card_visa", 21 | "confirm": "true", 22 | "description": "(created by Stripe CLI)" 23 | } 24 | }, 25 | { 26 | "name": "payment_method_updated", 27 | "path": "/v1/payment_methods/${setup_intent:payment_method}", 28 | "method": "post", 29 | "params": { 30 | "metadata": { 31 | "foo": "bar" 32 | } 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payout.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 2000, 12 | "confirm": "true", 13 | "currency": "usd", 14 | "description": "(created by Stripe CLI)", 15 | "payment_method": "pm_card_bypassPending", 16 | "payment_method_types": ["card"] 17 | } 18 | }, 19 | { 20 | "name": "payout", 21 | "path": "/v1/payouts", 22 | "method": "post", 23 | "params": { 24 | "amount": 1100, 25 | "currency": "usd", 26 | "description": "(created by Stripe CLI)" 27 | } 28 | } 29 | ] 30 | } 31 | 32 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/payout.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 2000, 12 | "confirm": "true", 13 | "currency": "usd", 14 | "description": "(created by Stripe CLI)", 15 | "payment_method": "pm_card_bypassPending", 16 | "payment_method_types": ["card"] 17 | } 18 | }, 19 | { 20 | "name": "payout", 21 | "path": "/v1/payouts", 22 | "method": "post", 23 | "params": { 24 | "amount": 1100, 25 | "currency": "usd", 26 | "description": "(created by Stripe CLI)" 27 | } 28 | }, 29 | { 30 | "name": "payout_updated", 31 | "path": "/v1/payouts/${payout:id}", 32 | "method": "post", 33 | "params": { 34 | "metadata": { 35 | "foo": "bar" 36 | } 37 | } 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/plan.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "plan", 8 | "path": "/v1/plans", 9 | "method": "post", 10 | "params": { 11 | "currency": "usd", 12 | "interval": "month", 13 | "amount": 2000, 14 | "product": { 15 | "name": "myproduct" 16 | } 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/plan.deleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "plan", 8 | "path": "/v1/plans", 9 | "method": "post", 10 | "params": { 11 | "currency": "usd", 12 | "interval": "month", 13 | "amount": 2000, 14 | "product": { 15 | "name": "myproduct" 16 | } 17 | } 18 | }, 19 | { 20 | "name": "plan_deleted", 21 | "path": "/v1/plans/${plan:id}", 22 | "method": "delete" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/plan.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "plan", 8 | "path": "/v1/plans", 9 | "method": "post", 10 | "params": { 11 | "currency": "usd", 12 | "interval": "month", 13 | "amount": 2000, 14 | "product": { 15 | "name": "myproduct" 16 | } 17 | } 18 | }, 19 | { 20 | "name": "plan_updated", 21 | "path": "/v1/plans/${plan:id}", 22 | "method": "post", 23 | "params": { 24 | "metadata": { 25 | "foo": "bar" 26 | } 27 | } 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/price.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "product", 8 | "path": "/v1/products", 9 | "method": "post", 10 | "params": { 11 | "name": "myproduct", 12 | "description": "(created by Stripe CLI)" 13 | } 14 | }, 15 | { 16 | "name": "price", 17 | "path": "/v1/prices", 18 | "method": "post", 19 | "params": { 20 | "product": "${product:id}", 21 | "unit_amount": "1500", 22 | "currency": "usd", 23 | "recurring[interval]": "month" 24 | } 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/price.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "product", 8 | "path": "/v1/products", 9 | "method": "post", 10 | "params": { 11 | "name": "myproduct", 12 | "description": "(created by Stripe CLI)" 13 | } 14 | }, 15 | { 16 | "name": "price", 17 | "path": "/v1/prices", 18 | "method": "post", 19 | "params": { 20 | "product": "${product:id}", 21 | "unit_amount": "1500", 22 | "currency": "usd", 23 | "recurring[interval]": "month" 24 | } 25 | }, 26 | { 27 | "name": "price_updated", 28 | "path": "/v1/prices/${price:id}", 29 | "method": "post", 30 | "params": { 31 | "metadata": { 32 | "foo": "bar" 33 | } 34 | } 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/product.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "product", 8 | "path": "/v1/products", 9 | "method": "post", 10 | "params": { 11 | "name": "myproduct", 12 | "description": "(created by Stripe CLI)" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/product.deleted.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "product", 8 | "path": "/v1/products", 9 | "method": "post", 10 | "params": { 11 | "name": "myproduct", 12 | "description": "(created by Stripe CLI)" 13 | } 14 | }, 15 | { 16 | "name": "product_deleted", 17 | "path": "/v1/products/${product:id}", 18 | "method": "delete" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/product.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "product", 8 | "path": "/v1/products", 9 | "method": "post", 10 | "params": { 11 | "name": "myproduct", 12 | "description": "(created by Stripe CLI)" 13 | } 14 | }, 15 | { 16 | "name": "product_updated", 17 | "path": "/v1/products/${product:id}", 18 | "method": "post", 19 | "params": { 20 | "metadata": { 21 | "foo": "bar" 22 | } 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/promotion_code.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "coupon", 8 | "path": "/v1/coupons", 9 | "method": "post", 10 | "params": { 11 | "percent_off": 20 12 | } 13 | }, 14 | { 15 | "name": "promotion_code", 16 | "path": "/v1/promotion_codes", 17 | "method": "post", 18 | "params": { 19 | "coupon": "${coupon:id}" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/promotion_code.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "coupon", 8 | "path": "/v1/coupons", 9 | "method": "post", 10 | "params": { 11 | "percent_off": 20 12 | } 13 | }, 14 | { 15 | "name": "promotion_code", 16 | "path": "/v1/promotion_codes", 17 | "method": "post", 18 | "params": { 19 | "coupon": "${coupon:id}" 20 | } 21 | }, 22 | { 23 | "name": "promotion_code_updated", 24 | "path": "/v1/promotion_codes/${promotion_code:id}", 25 | "method": "post", 26 | "params": { 27 | "metadata": { 28 | "foo": "bar" 29 | } 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/quote.accepted.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "source": "tok_visa" 13 | } 14 | }, 15 | { 16 | "name": "product", 17 | "path": "/v1/products", 18 | "method": "post", 19 | "params": { 20 | "name": "myproduct", 21 | "description": "(created by Stripe CLI)" 22 | } 23 | }, 24 | { 25 | "name": "price", 26 | "path": "/v1/prices", 27 | "method": "post", 28 | "params": { 29 | "product": "${product:id}", 30 | "unit_amount": "1500", 31 | "currency": "usd", 32 | "recurring[interval]": "month" 33 | } 34 | }, 35 | { 36 | "name": "quote", 37 | "path": "/v1/quotes", 38 | "method": "post", 39 | "params": { 40 | "description": "(created by Stripe CLI)", 41 | "customer": "${customer:id}", 42 | "line_items": { 43 | "0": { 44 | "price": "${price:id}", 45 | "quantity": 10 46 | } 47 | } 48 | } 49 | }, 50 | { 51 | "name": "quote", 52 | "path": "/v1/quotes/${quote:id}/finalize", 53 | "method": "post" 54 | }, 55 | { 56 | "name": "quote", 57 | "path": "/v1/quotes/${quote:id}/accept", 58 | "method": "post" 59 | } 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/quote.canceled.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "source": "tok_visa" 13 | } 14 | }, 15 | { 16 | "name": "product", 17 | "path": "/v1/products", 18 | "method": "post", 19 | "params": { 20 | "name": "myproduct", 21 | "description": "(created by Stripe CLI)" 22 | } 23 | }, 24 | { 25 | "name": "price", 26 | "path": "/v1/prices", 27 | "method": "post", 28 | "params": { 29 | "product": "${product:id}", 30 | "unit_amount": "1500", 31 | "currency": "usd", 32 | "recurring[interval]": "month" 33 | } 34 | }, 35 | { 36 | "name": "quote", 37 | "path": "/v1/quotes", 38 | "method": "post", 39 | "params": { 40 | "description": "(created by Stripe CLI)", 41 | "customer": "${customer:id}", 42 | "line_items": { 43 | "0": { 44 | "price": "${price:id}", 45 | "quantity": 10 46 | } 47 | } 48 | } 49 | }, 50 | { 51 | "name": "quote", 52 | "path": "/v1/quotes/${quote:id}/cancel", 53 | "method": "post" 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/quote.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "source": "tok_visa" 13 | } 14 | }, 15 | { 16 | "name": "product", 17 | "path": "/v1/products", 18 | "method": "post", 19 | "params": { 20 | "name": "myproduct", 21 | "description": "(created by Stripe CLI)" 22 | } 23 | }, 24 | { 25 | "name": "price", 26 | "path": "/v1/prices", 27 | "method": "post", 28 | "params": { 29 | "product": "${product:id}", 30 | "unit_amount": "1500", 31 | "currency": "usd", 32 | "recurring[interval]": "month" 33 | } 34 | }, 35 | { 36 | "name": "quote", 37 | "path": "/v1/quotes", 38 | "method": "post", 39 | "params": { 40 | "description": "(created by Stripe CLI)", 41 | "customer": "${customer:id}", 42 | "line_items": { 43 | "0": { 44 | "price": "${price:id}", 45 | "quantity": 10 46 | } 47 | } 48 | } 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/quote.finalized.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "source": "tok_visa" 13 | } 14 | }, 15 | { 16 | "name": "product", 17 | "path": "/v1/products", 18 | "method": "post", 19 | "params": { 20 | "name": "myproduct", 21 | "description": "(created by Stripe CLI)" 22 | } 23 | }, 24 | { 25 | "name": "price", 26 | "path": "/v1/prices", 27 | "method": "post", 28 | "params": { 29 | "product": "${product:id}", 30 | "unit_amount": "1500", 31 | "currency": "usd", 32 | "recurring[interval]": "month" 33 | } 34 | }, 35 | { 36 | "name": "quote", 37 | "path": "/v1/quotes", 38 | "method": "post", 39 | "params": { 40 | "description": "(created by Stripe CLI)", 41 | "customer": "${customer:id}", 42 | "line_items": { 43 | "0": { 44 | "price": "${price:id}", 45 | "quantity": 10 46 | } 47 | } 48 | } 49 | }, 50 | { 51 | "name": "quote", 52 | "path": "/v1/quotes/${quote:id}/finalize", 53 | "method": "post" 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/reporting.report_run.succeeded.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "reporting", 8 | "path": "/v1/reporting/report_runs", 9 | "method": "post", 10 | "params": { 11 | "report_type":"balance.summary.1", 12 | "parameters": { 13 | "interval_start": 1641045832, 14 | "interval_end": 1646143432 15 | } 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/setup_intent.canceled.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "setup_intent", 8 | "path": "/v1/setup_intents", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "payment_method_types": ["card"] 13 | } 14 | }, 15 | { 16 | "name": "setup_intent_canceled", 17 | "path": "/v1/setup_intents/${setup_intent:id}/cancel", 18 | "method": "post", 19 | "params": { 20 | "cancellation_reason": "requested_by_customer" 21 | } 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/setup_intent.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "setup_intent", 8 | "path": "/v1/setup_intents", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "payment_method_types": ["card"] 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/setup_intent.requires_action.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "_meta": { 4 | "template_version": 0 5 | }, 6 | "fixtures": [ 7 | { 8 | "name": "setup_intent", 9 | "path": "/v1/setup_intents", 10 | "method": "post", 11 | "params": { 12 | "confirm": "true", 13 | "description": "(created by Stripe CLI)", 14 | "payment_method": "pm_card_authenticationRequired" 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/setup_intent.setup_failed.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "setup_intent", 8 | "expected_error_type": "card_error", 9 | "path": "/v1/setup_intents", 10 | "method": "post", 11 | "params": { 12 | "confirm": "true", 13 | "description": "(created by Stripe CLI)", 14 | "payment_method": "pm_card_chargeDeclined", 15 | "payment_method_types": ["card"] 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/setup_intent.succeeded.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "setup_intent", 8 | "path": "/v1/setup_intents", 9 | "method": "post", 10 | "params": { 11 | "confirm": true, 12 | "description": "(created by Stripe CLI)", 13 | "payment_method": "pm_card_visa", 14 | "payment_method_types": ["card"] 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/subscription_schedule.canceled.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "source": "tok_visa" 13 | } 14 | }, 15 | { 16 | "name": "product", 17 | "path": "/v1/products", 18 | "method": "post", 19 | "params": { 20 | "name": "myproduct", 21 | "description": "(created by Stripe CLI)" 22 | } 23 | }, 24 | { 25 | "name": "price", 26 | "path": "/v1/prices", 27 | "method": "post", 28 | "params": { 29 | "product": "${product:id}", 30 | "unit_amount": "1500", 31 | "currency": "usd", 32 | "recurring[interval]": "month" 33 | } 34 | }, 35 | { 36 | "name": "subscription_schedule", 37 | "path": "/v1/subscription_schedules", 38 | "method": "post", 39 | "params": { 40 | "customer": "${customer:id}", 41 | "start_date": "now", 42 | "phases": { 43 | "0": { 44 | "iterations": 1, 45 | "items": [ 46 | { 47 | "price": "${price:id}", 48 | "quantity": 1 49 | } 50 | ] 51 | }, 52 | "1": { 53 | "iterations": 1, 54 | "items": [ 55 | { 56 | "price": "${price:id}", 57 | "quantity": 2 58 | } 59 | ] 60 | } 61 | } 62 | } 63 | }, 64 | { 65 | "name": "subscription_schedule_canceled", 66 | "path": "/v1/subscription_schedules/${subscription_schedule:id}/cancel", 67 | "method": "post" 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/subscription_schedule.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "source": "tok_visa" 13 | } 14 | }, 15 | { 16 | "name": "product", 17 | "path": "/v1/products", 18 | "method": "post", 19 | "params": { 20 | "name": "myproduct", 21 | "description": "(created by Stripe CLI)" 22 | } 23 | }, 24 | { 25 | "name": "price", 26 | "path": "/v1/prices", 27 | "method": "post", 28 | "params": { 29 | "product": "${product:id}", 30 | "unit_amount": "1500", 31 | "currency": "usd", 32 | "recurring[interval]": "month" 33 | } 34 | }, 35 | { 36 | "name": "subscription_schedule", 37 | "path": "/v1/subscription_schedules", 38 | "method": "post", 39 | "params": { 40 | "customer": "${customer:id}", 41 | "start_date": "now", 42 | "phases": { 43 | "0": { 44 | "iterations": 1, 45 | "items": [ 46 | { 47 | "price": "${price:id}", 48 | "quantity": 1 49 | } 50 | ] 51 | }, 52 | "1": { 53 | "iterations": 1, 54 | "items": [ 55 | { 56 | "price": "${price:id}", 57 | "quantity": 2 58 | } 59 | ] 60 | } 61 | } 62 | } 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/subscription_schedule.released.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "description": "(created by Stripe CLI)", 12 | "source": "tok_visa" 13 | } 14 | }, 15 | { 16 | "name": "product", 17 | "path": "/v1/products", 18 | "method": "post", 19 | "params": { 20 | "name": "myproduct", 21 | "description": "(created by Stripe CLI)" 22 | } 23 | }, 24 | { 25 | "name": "price", 26 | "path": "/v1/prices", 27 | "method": "post", 28 | "params": { 29 | "product": "${product:id}", 30 | "unit_amount": "1500", 31 | "currency": "usd", 32 | "recurring[interval]": "month" 33 | } 34 | }, 35 | { 36 | "name": "subscription_schedule", 37 | "path": "/v1/subscription_schedules", 38 | "method": "post", 39 | "params": { 40 | "customer": "${customer:id}", 41 | "start_date": "now", 42 | "phases": { 43 | "0": { 44 | "iterations": 1, 45 | "items": [ 46 | { 47 | "price": "${price:id}", 48 | "quantity": 1 49 | } 50 | ] 51 | }, 52 | "1": { 53 | "iterations": 1, 54 | "items": [ 55 | { 56 | "price": "${price:id}", 57 | "quantity": 2 58 | } 59 | ] 60 | } 61 | } 62 | } 63 | }, 64 | { 65 | "name": "subscription_schedule_released", 66 | "path": "/v1/subscription_schedules/${subscription_schedule:id}/release", 67 | "method": "post" 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/tax_rate.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "tax_rate", 8 | "path": "/v1/tax_rates", 9 | "method": "post", 10 | "params": { 11 | "display_name": "test", 12 | "inclusive": true, 13 | "percentage": 10, 14 | "description": "(created by Stripe CLI)" 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/tax_rate.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "tax_rate", 8 | "path": "/v1/tax_rates", 9 | "method": "post", 10 | "params": { 11 | "display_name": "test", 12 | "inclusive": true, 13 | "percentage": 10, 14 | "description": "(created by Stripe CLI)" 15 | } 16 | }, 17 | { 18 | "name": "tax_rate_updated", 19 | "path": "/v1/tax_rates/${tax_rate:id}", 20 | "method": "post", 21 | "params": { 22 | "metadata": { 23 | "foo": "bar" 24 | } 25 | } 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/transfer.created.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 1000, 12 | "currency": "usd", 13 | "payment_method": "pm_card_bypassPending", 14 | "confirm": "true" 15 | } 16 | }, 17 | { 18 | "name": "account", 19 | "path": "/v1/accounts", 20 | "method": "post" 21 | }, 22 | { 23 | "name": "transfer", 24 | "path": "/v1/transfers", 25 | "method": "post", 26 | "params": { 27 | "destination": "${account:id}", 28 | "amount": 1000, 29 | "currency": "usd" 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/transfer.reversed.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 1000, 12 | "currency": "usd", 13 | "payment_method": "pm_card_bypassPending", 14 | "confirm": "true" 15 | } 16 | }, 17 | { 18 | "name": "account", 19 | "path": "/v1/accounts", 20 | "method": "post" 21 | }, 22 | { 23 | "name": "transfer", 24 | "path": "/v1/transfers", 25 | "method": "post", 26 | "params": { 27 | "destination": "${account:id}", 28 | "amount": 1000, 29 | "currency": "usd" 30 | } 31 | }, 32 | { 33 | "name": "transfer_reverse", 34 | "path": "/v1/transfers/${transfer:id}/reversals", 35 | "method": "post", 36 | "params": { 37 | "amount": 100 38 | } 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/transfer.updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "payment_intent", 8 | "path": "/v1/payment_intents", 9 | "method": "post", 10 | "params": { 11 | "amount": 1000, 12 | "currency": "usd", 13 | "payment_method": "pm_card_bypassPending", 14 | "confirm": "true" 15 | } 16 | }, 17 | { 18 | "name": "account", 19 | "path": "/v1/accounts", 20 | "method": "post" 21 | }, 22 | { 23 | "name": "transfer", 24 | "path": "/v1/transfers", 25 | "method": "post", 26 | "params": { 27 | "destination": "${account:id}", 28 | "amount": 1000, 29 | "currency": "usd" 30 | } 31 | }, 32 | { 33 | "name": "transfer_update", 34 | "path": "/v1/transfers/${transfer:id}", 35 | "method": "post", 36 | "params": { 37 | "metadata": { 38 | "foo": "bar" 39 | } 40 | } 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /pkg/fixtures/triggers/v1.billing.meter.no_meter_found.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "billing_meter_event_session", 8 | "path": "/v2/billing/meter_event_session", 9 | "method": "post", 10 | "headers": { 11 | "Content-Type": "application/json", 12 | "Stripe-Version": "2024-09-30.acacia" 13 | }, 14 | "params": {} 15 | }, 16 | { 17 | "name": "create_billing_meter_event_stream", 18 | "path": "/v2/billing/meter_event_stream", 19 | "method": "post", 20 | "api_base": "https://meter-events.stripe.com", 21 | "headers": { 22 | "Content-Type": "application/json", 23 | "Stripe-Version": "2024-09-30.acacia", 24 | "Authorization": "Bearer ${billing_meter_event_session:authentication_token}" 25 | }, 26 | "params": { 27 | "events": [ 28 | { 29 | "event_name": "${generate-uuid}", 30 | "timestamp":"${time-now-RFC3339}", 31 | "payload": { 32 | "value":"10" 33 | } 34 | } 35 | ] 36 | } 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /pkg/gen/events_list.go.tpl: -------------------------------------------------------------------------------- 1 | // This file is generated; DO NOT EDIT. 2 | 3 | package proxy 4 | 5 | var validEvents = map[string]bool{ {{ range $_, $nsName := .Events }} 6 | "{{ $nsName }}": true, {{end}} 7 | } 8 | 9 | var validThinEvents = map[string]bool{ {{ range $_, $nsName := .ThinEvents }} 10 | "{{ $nsName }}": true, {{end}} 11 | } 12 | 13 | var validPreviewEvents = map[string]bool{ {{ range $_, $nsName := .PreviewEvents }} 14 | "{{ $nsName }}": true, {{end}} 15 | } 16 | -------------------------------------------------------------------------------- /pkg/gen/stripe_version_header.go.tpl: -------------------------------------------------------------------------------- 1 | // This file is generated; DO NOT EDIT. 2 | 3 | package requests 4 | 5 | // StripeVersionHeaderValue is the api version header value 6 | const StripeVersionHeaderValue = "{{ .StripeVersion }}" 7 | 8 | // StripePreviewVersionHeaderValue is the api version header value for preview features 9 | const StripePreviewVersionHeaderValue = "{{ .StripePreviewVersion }}" -------------------------------------------------------------------------------- /pkg/git/git.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "github.com/go-git/go-git/v5" 5 | ) 6 | 7 | // Operations contains the behaviors of the internal git package 8 | type Operations struct{} 9 | 10 | // Interface defines the behaviors of the internal git package 11 | type Interface interface { 12 | Clone(string, string) error 13 | Pull(string) error 14 | } 15 | 16 | // Clone clones a repo locally, returns an error if it fails 17 | func (g Operations) Clone(appCachePath, app string) error { 18 | _, err := git.PlainClone(appCachePath, false, &git.CloneOptions{ 19 | URL: app, 20 | }) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | return nil 26 | } 27 | 28 | // Pull will update the changes for the provided repo or fails 29 | func (g Operations) Pull(appCachePath string) error { 30 | repo, err := git.PlainOpen(appCachePath) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | err = repo.Fetch(&git.FetchOptions{ 36 | RemoteName: "origin", 37 | Force: true, 38 | }) 39 | if err != nil { 40 | switch e := err.Error(); e { 41 | case git.NoErrAlreadyUpToDate.Error(): 42 | break 43 | default: 44 | return err 45 | } 46 | } 47 | 48 | worktree, err := repo.Worktree() 49 | if err != nil { 50 | return err 51 | } 52 | 53 | err = worktree.Reset(&git.ResetOptions{ 54 | Mode: git.HardReset, 55 | }) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | err = worktree.Pull(&git.PullOptions{ 61 | Force: true, 62 | }) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /pkg/login/acct/retrieve_account.go: -------------------------------------------------------------------------------- 1 | package acct 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "net/url" 7 | 8 | "github.com/stripe/stripe-cli/pkg/stripe" 9 | ) 10 | 11 | // Account is the most outer layer of the json response from Stripe 12 | type Account struct { 13 | ID string `json:"id"` 14 | Settings Settings `json:"settings"` 15 | } 16 | 17 | // Settings is within the Account json response from Stripe 18 | type Settings struct { 19 | Dashboard Dashboard `json:"dashboard"` 20 | } 21 | 22 | // Dashboard is within the Settings json response from Stripe 23 | type Dashboard struct { 24 | DisplayName string `json:"display_name"` 25 | } 26 | 27 | // GetUserAccount retrieves the account information 28 | func GetUserAccount(ctx context.Context, baseURL string, apiKey string) (*Account, error) { 29 | parsedBaseURL, err := url.Parse(baseURL) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | client := &stripe.Client{ 35 | BaseURL: parsedBaseURL, 36 | APIKey: apiKey, 37 | } 38 | 39 | resp, err := client.PerformRequest(ctx, "GET", "/v1/account", "", nil) 40 | 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | defer resp.Body.Close() 46 | 47 | account := &Account{} 48 | 49 | err = json.NewDecoder(resp.Body).Decode(account) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | return account, nil 55 | } 56 | -------------------------------------------------------------------------------- /pkg/login/acct/retrieve_account_test.go: -------------------------------------------------------------------------------- 1 | package acct 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | const testName = "test_name" 14 | 15 | func TestGetAccount(t *testing.T) { 16 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 17 | require.Equal(t, "GET", r.Method) 18 | 19 | account := &Account{ 20 | ID: "acct_123", 21 | } 22 | account.Settings.Dashboard.DisplayName = testName 23 | 24 | w.WriteHeader(http.StatusOK) 25 | w.Header().Set("Content-Type", "application/json") 26 | json.NewEncoder(w).Encode(account) 27 | })) 28 | defer ts.Close() 29 | 30 | acc, err := GetUserAccount(context.Background(), ts.URL, "sk_test_123") 31 | require.NoError(t, err) 32 | require.Equal( 33 | t, 34 | testName, 35 | acc.Settings.Dashboard.DisplayName, 36 | ) 37 | require.Equal( 38 | t, 39 | "acct_123", 40 | acc.ID, 41 | ) 42 | } 43 | 44 | func TestGetAccountNoDisplayName(t *testing.T) { 45 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 46 | require.Equal(t, "GET", r.Method) 47 | 48 | account := &Account{ 49 | ID: "acct_123", 50 | } 51 | 52 | w.WriteHeader(http.StatusOK) 53 | w.Header().Set("Content-Type", "application/json") 54 | json.NewEncoder(w).Encode(account) 55 | })) 56 | defer ts.Close() 57 | 58 | acc, err := GetUserAccount(context.Background(), ts.URL, "sk_test_123") 59 | require.NoError(t, err) 60 | require.Equal( 61 | t, 62 | "", 63 | acc.Settings.Dashboard.DisplayName, 64 | ) 65 | require.Equal( 66 | t, 67 | "acct_123", 68 | acc.ID, 69 | ) 70 | } 71 | -------------------------------------------------------------------------------- /pkg/login/async.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | // AsyncInputReader is an interface that has an async version of scanln 9 | type AsyncInputReader interface { 10 | scanln(ch chan int) 11 | } 12 | 13 | // AsyncStdinReader implements scanln(ch chan int), an async version of scanln 14 | type AsyncStdinReader struct { 15 | } 16 | 17 | func (r AsyncStdinReader) scanln(ch chan int) { 18 | n, _ := fmt.Fscanln(os.Stdin) 19 | ch <- n 20 | } 21 | -------------------------------------------------------------------------------- /pkg/login/keys/configurer.go: -------------------------------------------------------------------------------- 1 | package keys 2 | 3 | import ( 4 | "github.com/spf13/afero" 5 | 6 | "github.com/stripe/stripe-cli/pkg/config" 7 | "github.com/stripe/stripe-cli/pkg/validators" 8 | ) 9 | 10 | // Configurer is an interface for saving login details 11 | type Configurer interface { 12 | SaveLoginDetails(response *PollAPIKeyResponse) error 13 | } 14 | 15 | // RAKConfigurer saves login details into the filesystem after the user has gone through the login flow 16 | type RAKConfigurer struct { 17 | cfg *config.Config 18 | fs afero.Fs 19 | } 20 | 21 | // NewRAKConfigurer returns a new RAKConfigurer 22 | func NewRAKConfigurer(cfg *config.Config, fs afero.Fs) *RAKConfigurer { 23 | return &RAKConfigurer{ 24 | cfg: cfg, 25 | fs: fs, 26 | } 27 | } 28 | 29 | // SaveLoginDetails function sets config for this profile. 30 | func (c *RAKConfigurer) SaveLoginDetails(response *PollAPIKeyResponse) error { 31 | validateErr := validators.APIKey(response.TestModeAPIKey) 32 | if validateErr != nil { 33 | return validateErr 34 | } 35 | 36 | c.cfg.Profile.LiveModeAPIKey = response.LiveModeAPIKey 37 | c.cfg.Profile.LiveModePublishableKey = response.LiveModePublishableKey 38 | c.cfg.Profile.TestModeAPIKey = response.TestModeAPIKey 39 | c.cfg.Profile.TestModePublishableKey = response.TestModePublishableKey 40 | c.cfg.Profile.DisplayName = response.AccountDisplayName 41 | c.cfg.Profile.AccountID = response.AccountID 42 | 43 | profileErr := c.cfg.Profile.CreateProfile() 44 | if profileErr != nil { 45 | return profileErr 46 | } 47 | 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /pkg/login/keys/configurer_test.go: -------------------------------------------------------------------------------- 1 | package keys 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/spf13/afero" 8 | "github.com/spf13/viper" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | 12 | "github.com/stripe/stripe-cli/pkg/config" 13 | ) 14 | 15 | func TestSaveLoginDetails(t *testing.T) { 16 | profilesFile := filepath.Join(t.TempDir(), "stripe", "config.toml") 17 | c := &config.Config{ 18 | LogLevel: "info", 19 | Profile: config.Profile{ 20 | ProfileName: "tests", 21 | }, 22 | ProfilesFile: profilesFile, 23 | } 24 | c.InitConfig() 25 | 26 | configurer := NewRAKConfigurer(c, afero.NewOsFs()) 27 | err := configurer.SaveLoginDetails(&PollAPIKeyResponse{ 28 | Redeemed: true, 29 | AccountID: "acct_123", 30 | AccountDisplayName: "", 31 | LiveModeAPIKey: "rk_live_1234567890000", 32 | TestModeAPIKey: "rk_test_1234567890000", 33 | LiveModePublishableKey: "pk_live_1234567890000", 34 | TestModePublishableKey: "pk_test_1234567890000", 35 | }) 36 | require.NoError(t, err) 37 | 38 | v := viper.New() 39 | v.SetConfigFile(profilesFile) 40 | 41 | err = v.ReadInConfig() 42 | require.NoError(t, err) 43 | 44 | assert.Equal(t, "acct_123", v.GetString("tests.account_id")) 45 | assert.Equal(t, "", v.GetString("tests.display_name")) 46 | assert.Equal(t, "rk_live_*********0000", v.GetString("tests.live_mode_api_key")) 47 | assert.Equal(t, "pk_live_1234567890000", v.GetString("tests.live_mode_pub_key")) 48 | assert.Equal(t, "rk_test_1234567890000", v.GetString("tests.test_mode_api_key")) 49 | assert.Equal(t, "pk_test_1234567890000", v.GetString("tests.test_mode_pub_key")) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/login/keys/keytransfer.go: -------------------------------------------------------------------------------- 1 | package keys 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/stripe/stripe-cli/pkg/login/acct" 8 | ) 9 | 10 | // AsyncPollResult is the data returned from polling for keys 11 | type AsyncPollResult struct { 12 | TestModeAPIKey string 13 | Account *acct.Account 14 | Err error 15 | } 16 | 17 | // KeyTransfer handles polling for API keys 18 | type KeyTransfer interface { 19 | AsyncPollKey(ctx context.Context, pollURL string, interval time.Duration, maxAttempts int, ch chan AsyncPollResult) 20 | } 21 | 22 | // RAKTransfer implements KeyTransfer to poll for RAKs 23 | type RAKTransfer struct { 24 | configurer Configurer 25 | } 26 | 27 | // NewRAKTransfer creates a new RAKTransfer object 28 | func NewRAKTransfer(configurer Configurer) *RAKTransfer { 29 | return &RAKTransfer{ 30 | configurer: configurer, 31 | } 32 | } 33 | 34 | // AsyncPollKey polls for RAKs 35 | func (rt *RAKTransfer) AsyncPollKey(ctx context.Context, pollURL string, interval time.Duration, maxAttempts int, ch chan AsyncPollResult) { 36 | defer close(ch) 37 | 38 | response, account, err := PollForKey(ctx, pollURL, interval, maxAttempts) 39 | if err != nil { 40 | ch <- AsyncPollResult{ 41 | TestModeAPIKey: "", 42 | Account: nil, 43 | Err: err, 44 | } 45 | return 46 | } 47 | 48 | err = rt.configurer.SaveLoginDetails(response) 49 | if err != nil { 50 | ch <- AsyncPollResult{ 51 | TestModeAPIKey: "", 52 | Account: nil, 53 | Err: err, 54 | } 55 | return 56 | } 57 | 58 | ch <- AsyncPollResult{ 59 | TestModeAPIKey: response.TestModeAPIKey, 60 | Account: account, 61 | Err: nil, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /pkg/login/links.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "net/url" 10 | 11 | "github.com/stripe/stripe-cli/pkg/stripe" 12 | "github.com/stripe/stripe-cli/pkg/version" 13 | ) 14 | 15 | // Links provides the URLs for the CLI to continue the login flow 16 | type Links struct { 17 | BrowserURL string `json:"browser_url"` 18 | PollURL string `json:"poll_url"` 19 | VerificationCode string `json:"verification_code"` 20 | } 21 | 22 | // GetLinks provides the URLs for the CLI to continue the login flow 23 | func GetLinks(ctx context.Context, baseURL string, deviceName string) (*Links, error) { 24 | parsedBaseURL, err := url.Parse(baseURL) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | client := &stripe.Client{ 30 | BaseURL: parsedBaseURL, 31 | } 32 | 33 | data := url.Values{} 34 | data.Set("client_version", version.Version) 35 | data.Set("device_name", deviceName) 36 | 37 | res, err := client.PerformRequest(ctx, http.MethodPost, stripeCLIAuthPath, data.Encode(), nil) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | defer res.Body.Close() 43 | 44 | bodyBytes, err := io.ReadAll(res.Body) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | if res.StatusCode != http.StatusOK { 50 | return nil, fmt.Errorf("unexpected http status code: %d %s", res.StatusCode, string(bodyBytes)) 51 | } 52 | 53 | var links Links 54 | 55 | err = json.Unmarshal(bodyBytes, &links) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | return &links, nil 61 | } 62 | -------------------------------------------------------------------------------- /pkg/login/login.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/spf13/afero" 7 | 8 | "github.com/stripe/stripe-cli/pkg/config" 9 | "github.com/stripe/stripe-cli/pkg/login/keys" 10 | ) 11 | 12 | // Login is the main entrypoint for logging in to the CLI. 13 | func Login(ctx context.Context, baseURL string, config *config.Config) error { 14 | links, err := GetLinks(ctx, baseURL, config.Profile.DeviceName) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | configurer := keys.NewRAKConfigurer(config, afero.NewOsFs()) 20 | rt := keys.NewRAKTransfer(configurer) 21 | auth := NewAuthenticator(rt) 22 | return auth.Login(ctx, links) 23 | } 24 | -------------------------------------------------------------------------------- /pkg/login/login_message.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/stripe/stripe-cli/pkg/ansi" 9 | "github.com/stripe/stripe-cli/pkg/login/acct" 10 | ) 11 | 12 | // SuccessMessage returns the display message for a successfully authenticated user 13 | func SuccessMessage(ctx context.Context, account *acct.Account, baseURL string, apiKey string) (string, error) { 14 | // Account will be nil if user did interactive login 15 | if account == nil { 16 | acc, err := acct.GetUserAccount(ctx, baseURL, apiKey) 17 | if err != nil { 18 | return "", err 19 | } 20 | 21 | account = acc 22 | } 23 | 24 | color := ansi.Color(os.Stdout) 25 | 26 | displayName := account.Settings.Dashboard.DisplayName 27 | accountID := account.ID 28 | 29 | if displayName != "" && accountID != "" { 30 | return fmt.Sprintf( 31 | "Done! The Stripe CLI is configured for %s with account id %s\n", 32 | color.Bold(displayName), 33 | color.Bold(accountID), 34 | ), nil 35 | } 36 | 37 | if accountID != "" { 38 | return fmt.Sprintf( 39 | "Done! The Stripe CLI is configured for your account with account id %s\n", 40 | color.Bold(accountID), 41 | ), nil 42 | } 43 | 44 | return "Done! The Stripe CLI is configured\n", nil 45 | } 46 | -------------------------------------------------------------------------------- /pkg/logout/logout.go: -------------------------------------------------------------------------------- 1 | package logout 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/stripe/stripe-cli/pkg/config" 7 | ) 8 | 9 | // Logout function is used to clear the credentials set for the current Profile 10 | func Logout(config *config.Config) error { 11 | liveKey, _ := config.Profile.GetAPIKey(true) 12 | testKey, _ := config.Profile.GetAPIKey(false) 13 | 14 | if liveKey == "" && testKey == "" { 15 | fmt.Println("You are already logged out.") 16 | return nil 17 | } 18 | 19 | fmt.Println("Logging out...") 20 | 21 | profileName := config.Profile.ProfileName 22 | 23 | err := config.RemoveProfile(profileName) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | if profileName == "default" { 29 | fmt.Println("Credentials have been cleared for the default project.") 30 | } else { 31 | fmt.Printf("Credentials have been cleared for %s.\n", profileName) 32 | } 33 | 34 | return nil 35 | } 36 | 37 | // All function is used to clear the credentials on all profiles 38 | func All(cfg *config.Config) error { 39 | fmt.Println("Logging out...") 40 | 41 | err := cfg.RemoveAllProfiles() 42 | if err != nil { 43 | return err 44 | } 45 | 46 | fmt.Println("Credentials have been cleared for all projects.") 47 | 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /pkg/open/open.go: -------------------------------------------------------------------------------- 1 | package open 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | exec "golang.org/x/sys/execabs" 8 | ) 9 | 10 | var execCommand = exec.Command 11 | 12 | // Browser takes a url and opens it using the default browser on the operating system 13 | func Browser(url string) error { 14 | var err error 15 | 16 | switch runtime.GOOS { 17 | case "linux": 18 | err = execCommand("xdg-open", url).Start() 19 | case "windows": 20 | err = execCommand("rundll32", "url.dll,FileProtocolHandler", url).Start() 21 | case "darwin": 22 | err = execCommand("open", url).Start() 23 | default: 24 | err = fmt.Errorf("unsupported platform") 25 | } 26 | 27 | if err != nil { 28 | return err 29 | } 30 | 31 | return nil 32 | } 33 | 34 | // CanOpenBrowser determines if no browser is set in linux 35 | func CanOpenBrowser() bool { 36 | if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { 37 | return true 38 | } 39 | 40 | output, err := execCommand("xdg-settings", "get", "default-web-browser").Output() 41 | 42 | if err != nil { 43 | return false 44 | } 45 | 46 | if string(output) == "" { 47 | return false 48 | } 49 | 50 | return true 51 | } 52 | -------------------------------------------------------------------------------- /pkg/plugins/config_dev.go: -------------------------------------------------------------------------------- 1 | //go:build localdev 2 | // +build localdev 3 | 4 | package plugins 5 | 6 | func init() { 7 | PluginDev = true 8 | PluginsPath = "" 9 | } 10 | -------------------------------------------------------------------------------- /pkg/plugins/proto/main.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package proto; 3 | 4 | option go_package = "github.com/stripe/stripe-cli/plugins/proto"; 5 | 6 | service Main { 7 | rpc RunCommand(RunCommandRequest) returns (RunCommandResponse); 8 | } 9 | 10 | message RunCommandRequest { 11 | AdditionalInfo additional_info = 1; 12 | repeated string args = 2; 13 | } 14 | 15 | message RunCommandResponse { 16 | } 17 | 18 | message AdditionalInfo { 19 | IsTerminal is_terminal = 1; 20 | } 21 | 22 | message IsTerminal { 23 | bool stdin = 1; 24 | bool stdout = 2; 25 | bool stderr = 3; 26 | } 27 | -------------------------------------------------------------------------------- /pkg/plugins/test_artifacts/plugins-foo-1.toml: -------------------------------------------------------------------------------- 1 | # This is a made up manifest file for unit test purposes. 2 | # The checksums are the same for all OS versions of a single release for testing simplicity 3 | [[Plugin]] 4 | Shortname = "foo" 5 | Binary = "stripe-cli-foo" 6 | MagicCookieValue = "6C5CFD81-F43C-447B-BBCF-803FF31CD3D1" 7 | 8 | [[Plugin.Release]] 9 | Arch = "amd64" 10 | OS = "darwin" 11 | Version = "0.0.2" 12 | Sum = "125653c37803a51a048f6687f7f66d511be614f675f199cd6c71928b74875238" 13 | 14 | [[Plugin.Release]] 15 | Arch = "arm64" 16 | OS = "darwin" 17 | Version = "0.0.2" 18 | Sum = "125653c37803a51a048f6687f7f66d511be614f675f199cd6c71928b74875238" 19 | -------------------------------------------------------------------------------- /pkg/proxy/v2_stripe_event.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // V2EventPayload describes the payload from the server for a v2 event 9 | type V2EventPayload struct { 10 | Created string `json:"created"` 11 | Data json.RawMessage `json:"data,omitempty"` 12 | ID string `json:"id"` 13 | Object string `json:"object"` 14 | RelatedObject primaryRelatedObject `json:"related_object"` 15 | Type string `json:"type"` 16 | Context string `json:"context,omitempty"` 17 | } 18 | 19 | type primaryRelatedObject struct { 20 | ID string `json:"id"` 21 | Type string `json:"type"` 22 | URL string `json:"url"` 23 | } 24 | 25 | // URLForEventID builds a full URL from a V2StripeEvent ID. 26 | func (e *V2EventPayload) URLForEventID(cliEndpointID string) string { 27 | return fmt.Sprintf("https://dashboard.stripe.com/workbench/webhooks/%s?event=%s", cliEndpointID, e.ID) 28 | } 29 | 30 | // IsConnect returns true if this event is associated with a connected account 31 | func (e *V2EventPayload) IsConnect() bool { 32 | return e.Context != "" 33 | } 34 | -------------------------------------------------------------------------------- /pkg/requests/plugin.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "net/http" 7 | 8 | "github.com/stripe/stripe-cli/pkg/config" 9 | ) 10 | 11 | // PluginData contains the plugin download information 12 | type PluginData struct { 13 | PluginBaseURL string `json:"base_url"` 14 | AdditionalManifests []string `json:"additional_manifests,omitempty"` 15 | } 16 | 17 | // GetPluginData returns the plugin download information 18 | func GetPluginData(ctx context.Context, baseURL, apiVersion, apiKey string, profile *config.Profile) (PluginData, error) { 19 | params := &RequestParameters{ 20 | data: []string{}, 21 | version: apiVersion, 22 | } 23 | 24 | base := &Base{ 25 | Profile: profile, 26 | Method: http.MethodGet, 27 | SuppressOutput: true, 28 | APIBaseURL: baseURL, 29 | } 30 | // /v1/stripecli/get-plugin-url 31 | resp, err := base.MakeRequest(ctx, apiKey, "/v1/stripecli/get-plugin-url", params, make(map[string]interface{}), true, nil) 32 | if err != nil { 33 | return PluginData{}, err 34 | } 35 | 36 | data := PluginData{} 37 | json.Unmarshal(resp, &data) 38 | 39 | return data, nil 40 | } 41 | -------------------------------------------------------------------------------- /pkg/requests/request_tracer.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptrace" 6 | 7 | log "github.com/sirupsen/logrus" 8 | ) 9 | 10 | // TracedTransport is an http.RoundTripper that keeps track of the in-flight 11 | // request and implements hooks to report HTTP tracing events 12 | // this is a different RoundTripper implementation to stripe.verboseTransport 13 | // and is not designed for Stripe API requests 14 | type TracedTransport struct { 15 | current *http.Request 16 | } 17 | 18 | // RoundTrip wraps http.DefaultTransport.RoundTrip to keep track 19 | // of the current request 20 | func (t *TracedTransport) RoundTrip(req *http.Request) (*http.Response, error) { 21 | t.current = req 22 | return http.DefaultTransport.RoundTrip(req) 23 | } 24 | 25 | // GotConn will trace log each connection for the current request 26 | func (t *TracedTransport) GotConn(connInfo httptrace.GotConnInfo) { 27 | log.WithFields(log.Fields{ 28 | "prefix": "requests.TracedTransport", 29 | "connInfo": connInfo, 30 | }).Tracef("Connection trace for %v: %v", t.current.URL, connInfo) 31 | } 32 | 33 | // DNSDone will trace log each DNS lookup for the current request 34 | func (t *TracedTransport) DNSDone(dnsInfo httptrace.DNSDoneInfo) { 35 | log.WithFields(log.Fields{ 36 | "prefix": "requests.TracedTransport", 37 | "dnsInfo": dnsInfo, 38 | }).Tracef("DNS trace for %v", t.current.URL) 39 | } 40 | -------------------------------------------------------------------------------- /pkg/requests/stripe_version_header.go: -------------------------------------------------------------------------------- 1 | // This file is generated; DO NOT EDIT. 2 | 3 | package requests 4 | 5 | // StripeVersionHeaderValue is the api version header value 6 | const StripeVersionHeaderValue = "2025-09-30.clover" 7 | 8 | // StripePreviewVersionHeaderValue is the api version header value for preview features 9 | const StripePreviewVersionHeaderValue = "2025-09-30.preview" 10 | -------------------------------------------------------------------------------- /pkg/rpcservice/fixtures.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stripe/stripe-cli/pkg/fixtures" 7 | "github.com/stripe/stripe-cli/rpc" 8 | ) 9 | 10 | // Fixture returns the default fixture of given event in string format 11 | func (srv *RPCService) Fixture(ctx context.Context, req *rpc.FixtureRequest) (*rpc.FixtureResponse, error) { 12 | fixtureFilename := fixtures.Events[req.Event] 13 | f, err := fixtures.NewFixtureFromFile(nil, "", "", "", fixtureFilename, []string{}, []string{}, []string{}, []string{}, false) 14 | if err != nil { 15 | return &rpc.FixtureResponse{Fixture: ""}, err 16 | } 17 | return &rpc.FixtureResponse{Fixture: f.GetFixtureFileContent()}, nil 18 | } 19 | -------------------------------------------------------------------------------- /pkg/rpcservice/fixtures_test.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/stripe/stripe-cli/rpc" 10 | 11 | "google.golang.org/grpc" 12 | "google.golang.org/grpc/credentials/insecure" 13 | ) 14 | 15 | func TestFixturesReturnsData(t *testing.T) { 16 | ctx := withAuth(context.Background()) 17 | 18 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials())) 19 | if err != nil { 20 | t.Fatalf("Failed to dial bufnet: %v", err) 21 | } 22 | defer conn.Close() 23 | client := rpc.NewStripeCLIClient(conn) 24 | 25 | resp, err := client.Fixture(ctx, &rpc.FixtureRequest{Event: "customer.created"}) 26 | 27 | expected := rpc.FixtureResponse{ 28 | Fixture: `{ 29 | "_meta": { 30 | "template_version": 0, 31 | "exclude_metadata": false 32 | }, 33 | "fixtures": [ 34 | { 35 | "name": "customer", 36 | "expected_error_type": "", 37 | "path": "/v1/customers", 38 | "method": "post", 39 | "params": { 40 | "description": "(created by Stripe CLI)" 41 | } 42 | } 43 | ], 44 | "env": null 45 | }`, 46 | } 47 | 48 | assert.Nil(t, err) 49 | assert.Equal(t, expected.Fixture, resp.Fixture) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/rpcservice/login.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stripe/stripe-cli/pkg/login" 7 | "github.com/stripe/stripe-cli/pkg/stripe" 8 | "github.com/stripe/stripe-cli/rpc" 9 | ) 10 | 11 | var links *login.Links 12 | var getLinks = login.GetLinks 13 | 14 | // Login returns a URL and pairing code to complete the login for the Stripe CLI 15 | func (srv *RPCService) Login(ctx context.Context, req *rpc.LoginRequest) (*rpc.LoginResponse, error) { 16 | var err error 17 | 18 | links, err = getLinks(ctx, stripe.DefaultDashboardBaseURL, srv.cfg.UserCfg.Profile.DeviceName) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | return &rpc.LoginResponse{ 24 | Url: links.BrowserURL, 25 | PairingCode: links.VerificationCode, 26 | }, nil 27 | } 28 | -------------------------------------------------------------------------------- /pkg/rpcservice/login_status.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/spf13/afero" 7 | 8 | "github.com/stripe/stripe-cli/pkg/config" 9 | "github.com/stripe/stripe-cli/pkg/login/keys" 10 | "github.com/stripe/stripe-cli/rpc" 11 | 12 | "google.golang.org/grpc/codes" 13 | "google.golang.org/grpc/status" 14 | ) 15 | 16 | var pollForKey = keys.PollForKey 17 | var configureProfile = saveLoginDetails 18 | 19 | // LoginStatus returns when login is successful, or returns an error if failure or timeout. 20 | func (srv *RPCService) LoginStatus(ctx context.Context, req *rpc.LoginStatusRequest) (*rpc.LoginStatusResponse, error) { 21 | if links == nil || links.PollURL == "" { 22 | return nil, status.Error(codes.FailedPrecondition, "There is no login in progress.") 23 | } 24 | 25 | response, account, err := pollForKey(ctx, links.PollURL, 0, 0) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | err = configureProfile(srv.cfg.UserCfg, response) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | displayName := account.Settings.Dashboard.DisplayName 36 | accountID := account.ID 37 | 38 | return &rpc.LoginStatusResponse{ 39 | DisplayName: displayName, 40 | AccountId: accountID, 41 | }, nil 42 | } 43 | 44 | func saveLoginDetails(config *config.Config, response *keys.PollAPIKeyResponse) error { 45 | configurer := keys.NewRAKConfigurer(config, afero.NewOsFs()) 46 | return configurer.SaveLoginDetails(response) 47 | } 48 | -------------------------------------------------------------------------------- /pkg/rpcservice/rpc_service_test.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net" 7 | 8 | "github.com/stripe/stripe-cli/pkg/config" 9 | "github.com/stripe/stripe-cli/rpc" 10 | 11 | "google.golang.org/grpc/metadata" 12 | "google.golang.org/grpc/test/bufconn" 13 | ) 14 | 15 | const bufSize = 1024 * 1024 16 | 17 | var lis *bufconn.Listener 18 | 19 | func init() { 20 | lis = bufconn.Listen(bufSize) 21 | srv := New(&Config{ 22 | UserCfg: &config.Config{ 23 | Profile: config.Profile{ 24 | APIKey: "sk_test_12345", 25 | DeviceName: "rpc_test_device_name", 26 | }, 27 | }, 28 | }, nil) 29 | 30 | rpc.RegisterStripeCLIServer(srv.grpcServer, srv) 31 | 32 | go func() { 33 | if err := srv.grpcServer.Serve(lis); err != nil { 34 | log.Fatalf("Server exited with error: %v", err) 35 | } 36 | }() 37 | } 38 | 39 | func bufDialer(context.Context, string) (net.Conn, error) { 40 | return lis.Dial() 41 | } 42 | 43 | func withAuth(ctx context.Context) context.Context { 44 | md := metadata.New(map[string]string{requiredHeader: "1"}) 45 | return metadata.NewOutgoingContext(ctx, md) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/rpcservice/sample_configs.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc/codes" 7 | "google.golang.org/grpc/status" 8 | 9 | "github.com/stripe/stripe-cli/pkg/samples" 10 | "github.com/stripe/stripe-cli/rpc" 11 | ) 12 | 13 | // Make overridable for tests 14 | var fetchRawSampleIntegrations = func(req *rpc.SampleConfigsRequest) ([]samples.SampleConfigIntegration, error) { 15 | sampleManager, err := samples.NewSampleManager(nil) 16 | if err != nil { 17 | return nil, err 18 | } 19 | err = sampleManager.Initialize(req.SampleName) 20 | if err != nil { 21 | return nil, err 22 | } 23 | return sampleManager.SampleConfig.Integrations, nil 24 | } 25 | 26 | // SampleConfigs returns a list of available configs for a given Stripe sample. 27 | func (srv *RPCService) SampleConfigs(ctx context.Context, req *rpc.SampleConfigsRequest) (*rpc.SampleConfigsResponse, error) { 28 | rawSampleIntegrations, err := fetchRawSampleIntegrations(req) 29 | 30 | if err != nil { 31 | return nil, status.Errorf(codes.Internal, "Failed to fetch configs for sample %s: %v", req.SampleName, err) 32 | } 33 | 34 | formattedSampleIntegrations := make([]*rpc.SampleConfigsResponse_Integration, len(rawSampleIntegrations)) 35 | for i, s := range rawSampleIntegrations { 36 | formattedSampleIntegrations[i] = &rpc.SampleConfigsResponse_Integration{ 37 | IntegrationName: s.Name, 38 | Clients: s.Clients, 39 | Servers: s.Servers, 40 | } 41 | } 42 | 43 | return &rpc.SampleConfigsResponse{ 44 | Integrations: formattedSampleIntegrations, 45 | }, nil 46 | } 47 | -------------------------------------------------------------------------------- /pkg/rpcservice/samples_list.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc/codes" 7 | "google.golang.org/grpc/status" 8 | 9 | "github.com/stripe/stripe-cli/pkg/samples" 10 | "github.com/stripe/stripe-cli/rpc" 11 | ) 12 | 13 | // Make overridable for tests 14 | var fetchRawSamplesList = func() (map[string]*samples.SampleData, error) { 15 | return samples.GetSamples("list") 16 | } 17 | 18 | // SamplesList returns a list of available Stripe samples 19 | func (srv *RPCService) SamplesList(ctx context.Context, req *rpc.SamplesListRequest) (*rpc.SamplesListResponse, error) { 20 | rawSamplesList, err := fetchRawSamplesList() 21 | if err != nil { 22 | return nil, status.Errorf(codes.Internal, "Failed to fetch Stripe samples list: %v", err) 23 | } 24 | 25 | formattedSamplesList := make([]*rpc.SamplesListResponse_SampleData, 0, len(rawSamplesList)) 26 | for _, v := range rawSamplesList { 27 | formattedSamplesList = append(formattedSamplesList, &rpc.SamplesListResponse_SampleData{ 28 | Name: v.Name, 29 | Description: v.Description, 30 | Url: v.URL, 31 | }) 32 | } 33 | 34 | return &rpc.SamplesListResponse{ 35 | Samples: formattedSamplesList, 36 | }, nil 37 | } 38 | -------------------------------------------------------------------------------- /pkg/rpcservice/trigger.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stripe/stripe-cli/pkg/fixtures" 7 | "github.com/stripe/stripe-cli/pkg/stripe" 8 | "github.com/stripe/stripe-cli/rpc" 9 | 10 | "google.golang.org/grpc/codes" 11 | "google.golang.org/grpc/status" 12 | ) 13 | 14 | var baseURL = stripe.DefaultAPIBaseURL 15 | 16 | // Trigger triggers a Stripe event. 17 | func (srv *RPCService) Trigger(ctx context.Context, req *rpc.TriggerRequest) (*rpc.TriggerResponse, error) { 18 | apiKey, err := srv.cfg.UserCfg.Profile.GetAPIKey(false) 19 | if err != nil { 20 | return nil, status.Error(codes.Unauthenticated, err.Error()) 21 | } 22 | 23 | requestNames, err := fixtures.Trigger( 24 | ctx, 25 | req.Event, 26 | req.StripeAccount, 27 | baseURL, 28 | apiKey, 29 | req.Skip, 30 | req.Override, 31 | req.Add, 32 | req.Remove, 33 | req.Raw, 34 | req.ApiVersion, 35 | req.Edit, 36 | ) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | return &rpc.TriggerResponse{ 42 | Requests: requestNames, 43 | }, nil 44 | } 45 | -------------------------------------------------------------------------------- /pkg/rpcservice/triggers_list.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stripe/stripe-cli/pkg/fixtures" 7 | "github.com/stripe/stripe-cli/rpc" 8 | ) 9 | 10 | // TriggersList returns a list of available events for `Trigger`. 11 | func (srv *RPCService) TriggersList(ctx context.Context, req *rpc.TriggersListRequest) (*rpc.TriggersListResponse, error) { 12 | return &rpc.TriggersListResponse{Events: fixtures.EventNames()}, nil 13 | } 14 | -------------------------------------------------------------------------------- /pkg/rpcservice/triggers_list_test.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/stripe/stripe-cli/pkg/fixtures" 10 | "github.com/stripe/stripe-cli/rpc" 11 | 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/credentials/insecure" 14 | ) 15 | 16 | func TestTriggersListReturnsEvents(t *testing.T) { 17 | ctx := withAuth(context.Background()) 18 | 19 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials())) 20 | if err != nil { 21 | t.Fatalf("Failed to dial bufnet: %v", err) 22 | } 23 | defer conn.Close() 24 | client := rpc.NewStripeCLIClient(conn) 25 | 26 | resp, err := client.TriggersList(ctx, &rpc.TriggersListRequest{}) 27 | 28 | expected := rpc.TriggersListResponse{ 29 | Events: fixtures.EventNames(), 30 | } 31 | 32 | assert.Nil(t, err) 33 | assert.Equal(t, expected.Events, resp.Events) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/rpcservice/version.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stripe/stripe-cli/pkg/version" 7 | "github.com/stripe/stripe-cli/rpc" 8 | ) 9 | 10 | // Version returns the version of the Stripe CLI 11 | func (srv *RPCService) Version(ctx context.Context, req *rpc.VersionRequest) (*rpc.VersionResponse, error) { 12 | return &rpc.VersionResponse{Version: version.Version}, nil 13 | } 14 | -------------------------------------------------------------------------------- /pkg/rpcservice/version_test.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/stripe/stripe-cli/rpc" 10 | 11 | "google.golang.org/grpc" 12 | "google.golang.org/grpc/credentials/insecure" 13 | ) 14 | 15 | func TestVersionReturnsCLIVersion(t *testing.T) { 16 | ctx := withAuth(context.Background()) 17 | 18 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials())) 19 | if err != nil { 20 | t.Fatalf("Failed to dial bufnet: %v", err) 21 | } 22 | defer conn.Close() 23 | client := rpc.NewStripeCLIClient(conn) 24 | 25 | resp, err := client.Version(ctx, &rpc.VersionRequest{}) 26 | if err != nil { 27 | t.Fatalf("Version failed: %v", err) 28 | } 29 | 30 | expected := rpc.VersionResponse{ 31 | Version: "master", 32 | } 33 | 34 | assert.Equal(t, expected.Version, resp.Version) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/rpcservice/webhook_endpoint_create.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stripe/stripe-cli/pkg/requests" 7 | "github.com/stripe/stripe-cli/pkg/stripe" 8 | "github.com/stripe/stripe-cli/rpc" 9 | ) 10 | 11 | // WebhookEndpointCreate create a new webhook endpoint 12 | func (srv *RPCService) WebhookEndpointCreate(ctx context.Context, req *rpc.WebhookEndpointCreateRequest) (*rpc.WebhookEndpointCreateResponse, error) { 13 | userConfig := srv.cfg.UserCfg 14 | livemode := false 15 | 16 | key, err := userConfig.Profile.GetAPIKey(livemode) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | err = requests.WebhookEndpointCreate( 22 | ctx, 23 | stripe.DefaultAPIBaseURL, 24 | stripe.APIVersion, 25 | key, 26 | req.Url, 27 | req.Description, 28 | req.Connect, 29 | &userConfig.Profile, 30 | ) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | return &rpc.WebhookEndpointCreateResponse{}, nil 36 | } 37 | -------------------------------------------------------------------------------- /pkg/rpcservice/webhook_endpoints_list.go: -------------------------------------------------------------------------------- 1 | package rpcservice 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stripe/stripe-cli/pkg/requests" 7 | "github.com/stripe/stripe-cli/pkg/stripe" 8 | "github.com/stripe/stripe-cli/rpc" 9 | ) 10 | 11 | // WebhookEndpointsList returns a list of webhook endpoints. 12 | func (srv *RPCService) WebhookEndpointsList(ctx context.Context, req *rpc.WebhookEndpointsListRequest) (*rpc.WebhookEndpointsListResponse, error) { 13 | userConfig := srv.cfg.UserCfg 14 | livemode := false 15 | 16 | key, err := userConfig.Profile.GetAPIKey(livemode) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | endpoints := requests.WebhookEndpointsList(ctx, stripe.DefaultAPIBaseURL, stripe.APIVersion, key, &userConfig.Profile) 22 | 23 | formattedEndpoints := make([]*rpc.WebhookEndpointsListResponse_WebhookEndpointData, 0, len(endpoints.Data)) 24 | for _, v := range endpoints.Data { 25 | formattedEndpoints = append(formattedEndpoints, &rpc.WebhookEndpointsListResponse_WebhookEndpointData{ 26 | Application: v.Application, 27 | EnabledEvents: v.EnabledEvents, 28 | Url: v.URL, 29 | Status: v.Status, 30 | }) 31 | } 32 | 33 | return &rpc.WebhookEndpointsListResponse{Endpoints: formattedEndpoints}, nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/spec/spec_test.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestLoadSpec(t *testing.T) { 11 | data, err := LoadSpec("../../api/openapi-spec/spec3.sdk.json") 12 | require.NoError(t, err) 13 | require.NotEmpty(t, data) 14 | } 15 | 16 | func TestUnmarshal_Simple(t *testing.T) { 17 | var schema Schema 18 | 19 | data := []byte(`{"type": "string"}`) 20 | err := json.Unmarshal(data, &schema) 21 | require.NoError(t, err) 22 | require.Equal(t, "string", schema.Type) 23 | } 24 | 25 | func TestUnmarshal_UnsupportedField(t *testing.T) { 26 | var schema Schema 27 | 28 | // We don't support 'const' 29 | data := []byte(`{const: "hello"}`) 30 | err := json.Unmarshal(data, &schema) 31 | require.Error(t, err) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/stripe/verbosetransport_test.go: -------------------------------------------------------------------------------- 1 | package stripe 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | "net/http/httptest" 7 | "regexp" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestVerboseTransport(t *testing.T) { 14 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 15 | w.Header().Set("Request-Id", "req_123") 16 | w.Header().Set("Non-Whitelisted-Header", "foo") 17 | w.WriteHeader(http.StatusOK) 18 | })) 19 | defer ts.Close() 20 | 21 | var b bytes.Buffer 22 | 23 | httpTransport := &http.Transport{} 24 | tr := &verboseTransport{ 25 | Transport: httpTransport, 26 | Out: &b, 27 | PrintableHeaders: inspectHeaders, 28 | } 29 | client := &http.Client{Transport: tr} 30 | req, err := http.NewRequest("POST", ts.URL+"/test", nil) 31 | require.NoError(t, err) 32 | req.Header.Set("Authorization", "Bearer token") 33 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 34 | 35 | resp, err := client.Do(req) 36 | require.NoError(t, err) 37 | 38 | defer resp.Body.Close() 39 | 40 | out := b.String() 41 | require.Regexp(t, regexp.MustCompile("> POST http://(.+)/test\n"), out) 42 | require.Contains(t, out, "> Authorization: Bearer [REDACTED]\n") 43 | require.Contains(t, out, "> Content-Type: application/x-www-form-urlencoded\n") 44 | require.Contains(t, out, "< HTTP 200\n") 45 | require.Contains(t, out, "< Request-Id: req_123\n") 46 | require.NotContains(t, out, "Non-Whitelisted-Header") 47 | } 48 | -------------------------------------------------------------------------------- /pkg/stripeauth/messages.go: -------------------------------------------------------------------------------- 1 | package stripeauth 2 | 3 | // StripeCLISession is the API resource returned by Stripe when initiating 4 | // a new CLI session. 5 | type StripeCLISession struct { 6 | DisplayConnectFilterWarning bool `json:"display_connect_filter_warning"` 7 | ReconnectDelay int `json:"reconnect_delay"` 8 | Secret string `json:"secret"` 9 | WebSocketAuthorizedFeature string `json:"websocket_authorized_feature"` 10 | WebSocketID string `json:"websocket_id"` 11 | WebSocketURL string `json:"websocket_url"` 12 | DefaultVersion string `json:"default_version"` 13 | LatestVersion string `json:"latest_version"` 14 | DeviceToken string `json:"device_token"` 15 | } 16 | -------------------------------------------------------------------------------- /pkg/terminal/p400/diagnostics.go: -------------------------------------------------------------------------------- 1 | package p400 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | "net/http/httptrace" 7 | ) 8 | 9 | // Transport is used when we're tracing a Rabbit Service call, in order to surface DNS and connectivity related data / errors 10 | // it helps provide a more specific / succinct error to the user in order to be more helpful when they run into trouble 11 | type Transport struct { 12 | DNSIPs []net.IPAddr 13 | Err error 14 | } 15 | 16 | // RoundTrip is a hook called from http client tracing to inspect for specific tcp dialing issues when calling Rabbit Service. 17 | // It allows for a less verbose error than what occurs further down the call stack, and attaches it to the Transport instance 18 | // It returns the response and any error unmodified in order to continue the completion of the http client's request work 19 | func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { 20 | res, err := http.DefaultTransport.RoundTrip(req) 21 | if err != nil { 22 | t.Err = err 23 | } 24 | 25 | return res, err 26 | } 27 | 28 | // DNSDone is a hook called from http client tracing to extract any DNS service IP resolutions from the tcp dialing of Rabbit Service 29 | // it attaches this list of resolved IPs to the Transport instance so we can later check if any errors were due to DNS not resolving to a valid IP 30 | func (t *Transport) DNSDone(info httptrace.DNSDoneInfo) { 31 | t.DNSIPs = info.Addrs 32 | } 33 | -------------------------------------------------------------------------------- /pkg/terminal/p400/rabbit_service_test.go: -------------------------------------------------------------------------------- 1 | package p400 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestCreateRabbitServicePayload(t *testing.T) { 10 | tsCtx := TerminalSessionContext{ 11 | APIKey: "sk_123", 12 | DeviceInfo: DeviceInfo{ 13 | DeviceClass: "POS", 14 | DeviceUUID: "pos-1234", 15 | HostOSVersion: "Mac OS", 16 | HardwareModel: HardwareModel{ 17 | POSInfo: POSInfo{ 18 | Description: "Mac OS:StripeCLI", 19 | }, 20 | }, 21 | AppModel: AppModel{ 22 | AppID: "Stripe-CLI-Terminal-Quickstart", 23 | AppVersion: "https://stripe.com/docs/stripe-cli", 24 | }, 25 | }, 26 | } 27 | 28 | payload := CreateRabbitServicePayload("clearReaderDisplay", "base64string=", "txn>23456", tsCtx) 29 | require.NotEmpty(t, payload) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/terminal/p400/reader_constants.go: -------------------------------------------------------------------------------- 1 | package p400 2 | 3 | var ( 4 | readerURL = "https://%v.%v:4443/protojsonservice/JackRabbitService" 5 | stripeTerminalReadersPath = "/v1/terminal/readers?device_type=verifone_P400&limit=100&compatible_sdk_type=js&compatible_sdk_version=1.3.2" 6 | rpcSessionPath = "/v1/terminal/connection_tokens/generate_pos_rpc_session" 7 | stripeTerminalConnectionTokensPath = "/v1/terminal/connection_tokens" 8 | stripeTerminalRegisterPath = "/v1/terminal/readers" 9 | stripeCreatePaymentIntentPath = "/v1/payment_intents" 10 | stripeCapturePaymentIntentPath = "/v1/payment_intents/%v/capture" 11 | ) 12 | -------------------------------------------------------------------------------- /pkg/terminal/p400/reader_methods_test.go: -------------------------------------------------------------------------------- 1 | package p400 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestSetParentTraceID(t *testing.T) { 10 | expected := "txn!1234>activateTerminal!5678" 11 | traceid := SetParentTraceID(1234, 5678, "activateTerminal") 12 | 13 | require.Equal(t, expected, traceid, "they should be equal") 14 | } 15 | 16 | // Disabling this test, we're prepparing to deprecate and remove the command 17 | // func TestGeneratePosDeviceID(t *testing.T) { 18 | // var seed int64 = 12345 19 | // 20 | // expected := "pos-vehnedrwfja" 21 | // posid := GeneratePOSDeviceID(seed) 22 | // 23 | // require.Equal(t, expected, posid, "they should be equal") 24 | // } 25 | 26 | func TestSetTransactionContext(t *testing.T) { 27 | tsCtx := TerminalSessionContext{ 28 | DeviceInfo: DeviceInfo{ 29 | DeviceUUID: "pos-isjlqargbit", 30 | }, 31 | } 32 | transCtx := SetTransactionContext(tsCtx) 33 | 34 | require.NotNil(t, transCtx.TerminalID, "should have TerminalID field") 35 | require.NotNil(t, transCtx.OperatorID, "should have OperatorID field") 36 | require.NotNil(t, transCtx.StartTime, "should have StartTime field") 37 | require.NotNil(t, transCtx.TransactionID, "should have TransactionID field") 38 | } 39 | -------------------------------------------------------------------------------- /pkg/terminal/reader_list.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | // ReaderData contains information about a specific Stripe compatible reader. An example is the Verifone P400. 4 | type ReaderData struct { 5 | Name string 6 | URL string 7 | Description string 8 | } 9 | 10 | var readerVerifoneP400 = &ReaderData{ 11 | Name: "Verifone P400", 12 | Description: "The Verifone P400 is a countertop reader for web-based Stripe Terminal apps. It connects to the Stripe Terminal SDK over the internet. This reader is compatible with JavaScript SDK.", 13 | URL: "https://www.verifone.com/sites/default/files/2018-01/p400_datasheet_ltr_013018.pdf", 14 | } 15 | 16 | // ReaderList is a map containing all of the Stripe compatible reader types that we support in the CLI. 17 | var ReaderList = map[string]*ReaderData{ 18 | "verifone-p400": readerVerifoneP400, 19 | } 20 | 21 | // ReaderNames is a function that uses ReaderList to extract the human friendly names of the CLI supported readers. 22 | // it returns a map of the human friendly reader names as strings 23 | func ReaderNames() []string { 24 | names := make([]string, 0, len(ReaderList)) 25 | for index := range ReaderList { 26 | names = append(names, ReaderList[index].Name) 27 | } 28 | 29 | return names 30 | } 31 | -------------------------------------------------------------------------------- /pkg/terminal/user_prompts.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/manifoldco/promptui" 7 | 8 | "github.com/stripe/stripe-cli/pkg/ansi" 9 | ) 10 | 11 | // ReaderTypeSelectPrompt prompts the user to choose which type of reader they want to set up 12 | // currently the only supported choice is the Verifone P400 13 | func ReaderTypeSelectPrompt(readers []string) (string, error) { 14 | selected, err := selectOptions("reader type", "Select which type of reader you’d like to set up", readers) 15 | 16 | if err != nil { 17 | return "", err 18 | } 19 | 20 | return selected, nil 21 | } 22 | 23 | func selectOptions(template string, label string, options []string) (string, error) { 24 | templates := &promptui.SelectTemplates{ 25 | Selected: ansi.Faint(fmt.Sprintf("✔ Selected %s: {{ . | bold }} ", template)), 26 | } 27 | prompt := promptui.Select{ 28 | Label: label, 29 | Items: options, 30 | Templates: templates, 31 | } 32 | 33 | _, result, err := prompt.Run() 34 | 35 | if err != nil { 36 | return "", err 37 | } 38 | 39 | return result, nil 40 | } 41 | -------------------------------------------------------------------------------- /pkg/useragent/uname_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package useragent 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | 10 | "golang.org/x/sys/unix" 11 | ) 12 | 13 | func trimNulls(input []byte) []byte { 14 | return bytes.Trim(input, "\x00") 15 | } 16 | 17 | func getUname() string { 18 | u := new(unix.Utsname) 19 | 20 | err := unix.Uname(u) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | return fmt.Sprintf("%s %s %s %s %s", trimNulls(u.Sysname[:]), trimNulls(u.Nodename[:]), trimNulls(u.Release[:]), trimNulls(u.Version[:]), trimNulls(u.Machine[:])) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/useragent/uname_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package useragent 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestUnameNotEmpty(t *testing.T) { 13 | u := getUname() 14 | t.Logf("%x", u) // For NULL trim paranoia 15 | require.NotEmpty(t, u) 16 | } 17 | 18 | func TestTrimNulls(t *testing.T) { 19 | input := [256]byte{0xff} 20 | t.Log(input) 21 | output := trimNulls(input[:]) 22 | t.Log(output) 23 | require.NotEqual(t, len(input), len(output)) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/useragent/uname_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package useragent 5 | 6 | func getUname() string { 7 | // TODO: if there is appetite for it in the community 8 | // add support for Windows GetSystemInfo 9 | return "" 10 | } 11 | -------------------------------------------------------------------------------- /pkg/validators/cmds_test.go: -------------------------------------------------------------------------------- 1 | package validators 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/spf13/cobra" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestNoArgs(t *testing.T) { 11 | c := &cobra.Command{Use: "c"} 12 | args := []string{} 13 | 14 | result := NoArgs(c, args) 15 | require.Nil(t, result) 16 | } 17 | 18 | func TestNoArgsWithArgs(t *testing.T) { 19 | c := &cobra.Command{Use: "c"} 20 | args := []string{"foo"} 21 | 22 | result := NoArgs(c, args) 23 | require.EqualError(t, result, "`c` does not take any positional arguments. See `c --help` for supported flags and usage") 24 | } 25 | 26 | func TestExactArgs(t *testing.T) { 27 | c := &cobra.Command{Use: "c"} 28 | args := []string{"foo"} 29 | 30 | result := ExactArgs(1)(c, args) 31 | require.Nil(t, result) 32 | } 33 | 34 | func TestExactArgsTooMany(t *testing.T) { 35 | c := &cobra.Command{Use: "c"} 36 | args := []string{"foo", "bar"} 37 | 38 | result := ExactArgs(1)(c, args) 39 | require.EqualError(t, result, "`c` requires exactly 1 positional argument. See `c --help` for supported flags and usage") 40 | } 41 | 42 | func TestExactArgsTooManyMoreThan1(t *testing.T) { 43 | c := &cobra.Command{Use: "c"} 44 | args := []string{"foo", "bar", "baz"} 45 | 46 | result := ExactArgs(2)(c, args) 47 | require.EqualError(t, result, "`c` requires exactly 2 positional arguments. See `c --help` for supported flags and usage") 48 | } 49 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/google/go-github/v28/github" 10 | log "github.com/sirupsen/logrus" 11 | 12 | "github.com/stripe/stripe-cli/pkg/ansi" 13 | ) 14 | 15 | // Version of the CLI. 16 | // This is set to the actual version by GoReleaser, identify by the 17 | // git tag assigned to the release. Versions built from source will 18 | // always show master. 19 | var Version = "master" 20 | 21 | // Template for the version string. 22 | var Template = fmt.Sprintf("stripe version %s\n", Version) 23 | 24 | // CheckLatestVersion makes a request to the GitHub API to pull the latest 25 | // release of the CLI 26 | func CheckLatestVersion() { 27 | // master is the dev version, we don't want to check against that every time 28 | if Version != "master" { 29 | s := ansi.StartNewSpinner("Checking for new versions...", os.Stdout) 30 | latest := getLatestVersion() 31 | 32 | ansi.StopSpinner(s, "", os.Stdout) 33 | 34 | if needsToUpgrade(Version, latest) { 35 | fmt.Println(ansi.Italic("A newer version of the Stripe CLI is available, please update to:"), ansi.Italic(latest)) 36 | } 37 | } 38 | } 39 | 40 | func needsToUpgrade(version, latest string) bool { 41 | return latest != "" && (strings.TrimPrefix(latest, "v") != strings.TrimPrefix(version, "v")) 42 | } 43 | 44 | func getLatestVersion() string { 45 | client := github.NewClient(nil) 46 | rep, _, err := client.Repositories.GetLatestRelease(context.Background(), "stripe", "stripe-cli") 47 | 48 | l := log.StandardLogger() 49 | 50 | if err != nil { 51 | // We don't want to fail any functionality or display errors for this 52 | // so fail silently and output to debug log 53 | l.Debug(err) 54 | return "" 55 | } 56 | 57 | return *rep.TagName 58 | } 59 | -------------------------------------------------------------------------------- /pkg/version/version_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestNeedsToUpgrade(t *testing.T) { 10 | require.False(t, needsToUpgrade("4.2.4.2", "v4.2.4.2")) 11 | require.False(t, needsToUpgrade("4.2.4.2", "4.2.4.2")) 12 | require.True(t, needsToUpgrade("4.2.4.2", "4.2.4.3")) 13 | require.True(t, needsToUpgrade("4.2.4.2", "v4.2.4.3")) 14 | require.True(t, needsToUpgrade("v4.2.4.2", "v4.2.4.3")) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/websocket/request_log_messages.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | // RequestLogEvent represents incoming request log event messages sent by Stripe. 4 | type RequestLogEvent struct { 5 | EventPayload string `json:"event_payload"` 6 | 7 | // RequestLogID is the `resp_` id for the response event which is used as the request log event throughout the system. 8 | // This is different from the `EventPayload.RequestID` which is the `req_` id for the user's actual request, which they 9 | // can use to find their request in the dashboard. 10 | RequestLogID string `json:"request_log_id"` 11 | Type string `json:"type"` 12 | } 13 | -------------------------------------------------------------------------------- /pkg/websocket/request_log_messages_test.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestUnmarshalRequestLogEvent(t *testing.T) { 11 | var data = `{"type": "request_log_event", "event_payload": "foo", "request_log_id": "resp_123"}` 12 | 13 | var msg IncomingMessage 14 | err := json.Unmarshal([]byte(data), &msg) 15 | require.NoError(t, err) 16 | 17 | require.NotNil(t, msg.RequestLogEvent) 18 | require.Nil(t, msg.WebhookEvent) 19 | 20 | require.Equal(t, "foo", msg.RequestLogEvent.EventPayload) 21 | require.Equal(t, "resp_123", msg.RequestLogEvent.RequestLogID) 22 | require.Equal(t, "request_log_event", msg.RequestLogEvent.Type) 23 | } 24 | -------------------------------------------------------------------------------- /rpc/events_resend.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | import "common.proto"; 6 | 7 | option go_package = "github.com/stripe/stripe-cli/rpc"; 8 | 9 | message EventsResendRequest { 10 | // The ID of the event to resend. 11 | string event_id = 1; 12 | 13 | // Resend the event to the given Stripe account. This is useful when testing a Connect platform. 14 | string account = 2; 15 | 16 | // Additional data to send with an API request. Supports setting nested values 17 | // (e.g nested[param]=value). 18 | repeated string data = 3; 19 | 20 | // Response attributes to expand inline (target nested values with nested[param]=value). 21 | repeated string expand = 4; 22 | 23 | // Set an idempotency key for the request, preventing the same request from replaying within 24 24 | // hours. 25 | string idempotency = 5; 26 | 27 | // Make a live request (by default, runs in test mode). 28 | bool live = 6; 29 | 30 | // Specify the Stripe account to use for this request. 31 | string stripe_account = 7; 32 | 33 | // Specify the Stripe API version to use for this request. 34 | string version = 8; 35 | 36 | // Resend the event to the given webhook endpoint ID. 37 | string webhook_endpoint = 9; 38 | } 39 | 40 | message EventsResendResponse { 41 | StripeEvent stripe_event = 1; 42 | } 43 | -------------------------------------------------------------------------------- /rpc/fixtures.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message FixtureRequest { 8 | // An event to get the default fixture for 9 | string event = 1; 10 | } 11 | 12 | message FixtureResponse { 13 | // default fixture for event 14 | string fixture = 1; 15 | } 16 | -------------------------------------------------------------------------------- /rpc/login.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message LoginRequest { 8 | } 9 | 10 | message LoginResponse { 11 | // The URL to complete the login. The client must open this in the browser to successfully log in. 12 | string url = 1; 13 | 14 | // The pairing code to verify your authentication with Stripe, e.g. excels-champ-wins-quaint 15 | string pairing_code = 2; 16 | } 17 | -------------------------------------------------------------------------------- /rpc/login_status.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message LoginStatusRequest { 8 | } 9 | 10 | message LoginStatusResponse { 11 | // ID of the Stripe account, e.g. acct_123 12 | string account_id = 1; 13 | 14 | // Display name of the Stripe account 15 | string display_name = 2; 16 | } 17 | -------------------------------------------------------------------------------- /rpc/sample_configs.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message SampleConfigsRequest { 8 | // Name of the sample, e.g. accept-a-card-payment 9 | string sample_name = 1; 10 | } 11 | 12 | message SampleConfigsResponse { 13 | message Integration { 14 | // Name of an available integration for this sample, e.g. "using-webhooks" 15 | string integration_name = 1; 16 | 17 | // List of available languages or platforms for the sample client, e.g. ["web", "android", "ios"] 18 | repeated string clients = 2; 19 | 20 | // List of available languages or platforms for the sample server, e.g. ["java", "node"] 21 | repeated string servers = 3; 22 | } 23 | 24 | // List of available integrations for this sample, e.g. the "accept-a-card-payment" sample 25 | // includes an integration that uses webhooks, a web client, and a node server. 26 | repeated Integration integrations = 1; 27 | } 28 | -------------------------------------------------------------------------------- /rpc/sample_create.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message SampleCreateRequest { 8 | // Name of the sample, e.g. accept-a-card-payment. Use the `SamplesList` method to get a list of 9 | // available samples. 10 | string sample_name = 1; 11 | 12 | // Name of the particular integration, e.g. using-webhooks. Use the `SampleConfigs` method to get 13 | // the available options. 14 | string integration_name = 2; 15 | 16 | // Platform or language for the client, e.g. web. Use the `SampleConfigs` method to get the 17 | // available options. 18 | string client = 3; 19 | 20 | // Platform or language for the server, e.g. node. Use the `SampleConfigs` method to get the 21 | // available options. 22 | string server = 4; 23 | 24 | // Path to clone the repo to. 25 | string path = 5; 26 | 27 | // If true, clear the local cache before creating the sample. 28 | bool force_refresh = 6; 29 | } 30 | 31 | message SampleCreateResponse { 32 | // Additional instructions for the sample after install. 33 | string post_install = 1; 34 | 35 | // Path to the sample. 36 | string path = 2; 37 | } 38 | -------------------------------------------------------------------------------- /rpc/samples_list.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message SamplesListRequest {} 8 | 9 | message SamplesListResponse { 10 | message SampleData { 11 | // Name of the sample, e.g. accept-a-card-payment 12 | string name = 1; 13 | 14 | // URL of the repo, e.g. https://github.com/stripe-samples/accept-a-card-payment 15 | string url = 2; 16 | 17 | // Description of the sample, e.g. Learn how to accept a basic card payment 18 | string description = 3; 19 | } 20 | 21 | // List of available Stripe samples 22 | repeated SampleData samples = 1; 23 | } 24 | -------------------------------------------------------------------------------- /rpc/trigger.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message TriggerRequest { 8 | // An event to trigger. Use `TriggersList` to see the available events. 9 | string event = 1; 10 | 11 | // Set a header identifying the connected account 12 | string stripe_account = 2; 13 | 14 | // Skip specific steps in the fixture 15 | repeated string skip = 3; 16 | 17 | // Override parameters in the fixture 18 | repeated string override = 4; 19 | 20 | // Add parameters in the fixture 21 | repeated string add = 5; 22 | 23 | // Remove parameters from the fixture 24 | repeated string remove = 6; 25 | 26 | // Raw fixture string 27 | string raw = 7; 28 | 29 | // Specify API Version for fixture 30 | string api_version = 8; 31 | 32 | // Whether or not to edit the fixture directly in git's default IDE 33 | bool edit = 9; 34 | } 35 | 36 | message TriggerResponse { 37 | // List of requests made during this trigger 38 | repeated string requests = 1; 39 | } 40 | -------------------------------------------------------------------------------- /rpc/triggers_list.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message TriggersListRequest {} 8 | 9 | message TriggersListResponse { 10 | // A list of supported events for `Trigger`. 11 | repeated string events = 1; 12 | } 13 | -------------------------------------------------------------------------------- /rpc/version.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message VersionRequest {} 8 | 9 | message VersionResponse { 10 | // The version of the Stripe CLI 11 | string version = 1; 12 | } 13 | -------------------------------------------------------------------------------- /rpc/webhook_endpoint_create.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message WebhookEndpointCreateRequest { 8 | // Webhook endpoint url 9 | string url = 1; 10 | 11 | // Webhook endpoint description 12 | string description = 2; 13 | 14 | // True to receive events from connected accounts, false otherwise 15 | bool connect = 3; 16 | } 17 | 18 | message WebhookEndpointCreateResponse { 19 | } 20 | -------------------------------------------------------------------------------- /rpc/webhook_endpoints_list.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package rpc; 4 | 5 | option go_package = "github.com/stripe/stripe-cli/rpc"; 6 | 7 | message WebhookEndpointsListRequest {} 8 | 9 | message WebhookEndpointsListResponse { 10 | message WebhookEndpointData { 11 | // Webhook endpoint application 12 | string application = 1; 13 | 14 | // Enabled events of the webhook endpoint 15 | repeated string enabledEvents = 2; 16 | 17 | // Webhook endpoint URL 18 | string url = 3; 19 | 20 | // Webhook endpoint status 21 | string status = 4; 22 | } 23 | 24 | // A list webhook endpoints 25 | repeated WebhookEndpointData endpoints = 1; 26 | } 27 | -------------------------------------------------------------------------------- /scripts/postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | stripe postinstall 4 | 5 | -------------------------------------------------------------------------------- /scripts/publish-to-artifactory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Adapted from https://github.com/leopardslab/dunner/blob/master/release/publish_deb_to_bintray.sh 4 | 5 | set -e 6 | 7 | FILENAME=$1 8 | VERSION=$2 9 | ARCH=$3 10 | FORMAT=$4 11 | 12 | REPO="stripe-cli-$FORMAT" 13 | PACKAGE="stripe" 14 | DISTRIBUTIONS="stable" 15 | COMPONENTS="main" 16 | 17 | if [ -z "$ARTIFACTORY_SECRET" ]; then 18 | echo "ARTIFACTORY_SECRET is not set" 19 | exit 1 20 | fi 21 | 22 | artifactoryUpload () { 23 | if [ "$ARCH" == "386" ]; then 24 | ARCH="i386" 25 | fi 26 | 27 | if [[ $FORMAT == "debian" ]] 28 | then 29 | if [ $ARCH == "x86_64" ]; then 30 | ARCH="amd64" 31 | fi 32 | 33 | echo "setting deployment to debian repo" 34 | UPLOAD_URL="https://stripe.jfrog.io/artifactory/$REPO-local/pool/$PACKAGE/$VERSION/$ARCH/$PACKAGE.deb;deb.distribution=$DISTRIBUTIONS;deb.component=$COMPONENTS;deb.architecture=$ARCH" 35 | elif [[ $FORMAT == "rpm" ]] 36 | then 37 | echo "setting deployment to rpm repo" 38 | UPLOAD_URL="https://stripe.jfrog.io/artifactory/$REPO-local/$PACKAGE/$VERSION/$ARCH/$PACKAGE.rpm" 39 | else 40 | echo "unrecognised package format" 41 | exit 1 42 | fi 43 | 44 | echo "Uploading $UPLOAD_URL" 45 | 46 | RESPONSE_CODE=$(curl -X PUT -T "$FILENAME" -H "Authorization: Bearer $ARTIFACTORY_SECRET" "$UPLOAD_URL" -I -s -w "%{http_code}" -o /dev/null) 47 | 48 | if [[ "$(echo "$RESPONSE_CODE" | head -c2)" != "20" ]]; then 49 | echo "Unable to upload, HTTP response code: $RESPONSE_CODE" 50 | exit 1 51 | fi 52 | 53 | echo "HTTP response code: $RESPONSE_CODE" 54 | } 55 | 56 | printMeta () { 57 | echo "Publishing: $PACKAGE" 58 | echo "Version to be uploaded: $VERSION" 59 | } 60 | 61 | printMeta 62 | artifactoryUpload 63 | -------------------------------------------------------------------------------- /scripts/sync-openapi-v2.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu -o pipefail 3 | 4 | pushd "$HOME/stripe/zoolander" 5 | 6 | # Pull master. 7 | echo "Bringing master up to date." 8 | git checkout master && git pull 9 | 10 | # Grab SHA so we can save this to a file for some kind of "paper trail". 11 | SHA=$(git rev-parse HEAD) 12 | 13 | echo "⏳ Retrieving v2 openapi spec..." 14 | 15 | # for apispecdump docs, see https://go/api-v2/dump 16 | ./scripts/api-services/apiv2 apispecdump --version="2025-09-30.clover" --format OPENAPI_JSON --variant CLI --out-file spec3.v2.sdk.json 17 | ./scripts/api-services/apiv2 apispecdump --version="2025-09-30.clover" --format OPENAPI_JSON --variant CLI_PUBLIC_PREVIEW --out-file spec3.v2.sdk.preview.json 18 | 19 | popd 20 | 21 | rm -f api/openapi-spec/spec3.v2.sdk.json 22 | rm -f api/openapi-spec/spec3.v2.sdk.preview.json 23 | 24 | echo "$SHA" > api/ZOOLANDER_SHA 25 | 26 | cp ~/stripe/zoolander/spec3.v2.sdk.json api/openapi-spec/ 27 | cp ~/stripe/zoolander/spec3.v2.sdk.preview.json api/openapi-spec/ 28 | 29 | echo "⏳ Generating resource commands..." 30 | 31 | make build 32 | 33 | echo "✅ Successfully generated resource commands and rebuilt CLI." 34 | 35 | --------------------------------------------------------------------------------