├── .devcontainer
├── Dockerfile
└── devcontainer.json
├── .dockerignore
├── .github
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── config.yml
│ ├── feature_request.md
│ └── housekeeping.md
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
├── labeler.yml
├── stale.yml
└── workflows
│ ├── codeql-analysis.yml
│ ├── documentation-links.yml
│ ├── labeler.yml
│ ├── linter.yml
│ ├── release.yml
│ └── tests.yml
├── .gitignore
├── .golangci.yaml
├── .goreleaser.yaml
├── CODEOWNERS
├── Dockerfile
├── LICENSE
├── README.md
├── SECURITY.md
├── Taskfile.yml
├── docs
├── README.md
├── pkg
│ ├── connect
│ │ └── README.md
│ ├── idempotency
│ │ └── README.md
│ └── pagination
│ │ └── README.md
├── v3-upgrade.md
└── v4-upgrade.md
├── go.mod
├── go.sum
├── lychee.toml
├── mollie
├── app_fee.go
├── balances.go
├── balances_test.go
├── captures.go
├── captures_test.go
├── chargebacks.go
├── chargebacks_test.go
├── client_links.go
├── client_links_test.go
├── clients.go
├── clients_test.go
├── common_types.go
├── common_types_test.go
├── config.go
├── config_test.go
├── custom_types.go
├── custom_types_test.go
├── customers.go
├── customers_test.go
├── doc.go
├── errors.go
├── errors_test.go
├── gift_cards.go
├── invoices.go
├── invoices_test.go
├── mandates.go
├── mandates_test.go
├── mollie.go
├── mollie_test.go
├── onboarding.go
├── onboarding_test.go
├── orders.go
├── orders_test.go
├── organizations.go
├── organizations_test.go
├── payment_details.go
├── payment_links.go
├── payment_links_test.go
├── payment_methods.go
├── payment_methods_test.go
├── payments.go
├── payments_test.go
├── permissions.go
├── permissions_test.go
├── profiles.go
├── profiles_test.go
├── refunds.go
├── refunds_test.go
├── settlements.go
├── settlements_test.go
├── shipments.go
├── shipments_test.go
├── subscriptions.go
├── subscriptions_test.go
├── terminals.go
├── terminals_test.go
├── vouchers.go
├── wallets.go
└── wallets_test.go
├── pkg
├── connect
│ ├── oauth2.go
│ └── oauth2_test.go
├── idempotency
│ ├── contract.go
│ ├── doc.go
│ ├── nop.go
│ ├── nop_test.go
│ ├── std.go
│ └── std_test.go
└── pagination
│ ├── doc.go
│ ├── pagination.go
│ └── pagination_test.go
└── testdata
├── balances.go
├── captures.go
├── chargebacks.go
├── client_links.go
├── customers.go
├── errors.go
├── invoices.go
├── mandates.go
├── methods.go
├── miscellaneous.go
├── onboarding.go
├── orders.go
├── organizations.go
├── partners.go
├── payment_links.go
├── payments.go
├── permissions.go
├── profiles.go
├── refunds.go
├── settlements.go
├── shipments.go
├── subscriptions.go
└── terminals.go
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG GO_VERSION=1-1-bullseye
2 |
3 | ARG VARIANT=1-bullseye
4 | FROM mcr.microsoft.com/vscode/devcontainers/go:${GO_VERSION}
5 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "base devcontainer",
3 | "build": {
4 | "dockerfile": "Dockerfile"
5 | },
6 | "capAdd": [
7 | "SYS_PTRACE"
8 | ],
9 | "securityOpt": [
10 | "seccomp=unconfined"
11 | ],
12 | "mounts": [
13 | {
14 | "source": "${localEnv:HOME}/.zshrc",
15 | "target": "/root/.zshrc",
16 | "type": "bind"
17 | },
18 | {
19 | "source": "${localEnv:HOME}/.config/starship.toml",
20 | "target": "/root/.config/starship.toml",
21 | "type": "bind"
22 | },
23 | {
24 | "source": "${localEnv:HOME}/.config/bat/config",
25 | "target": "/root/.config/bat/config",
26 | "type": "bind"
27 | }
28 | ],
29 | "features": {
30 | "ghcr.io/devcontainers-contrib/features/act-asdf:2": {},
31 | "ghcr.io/devcontainers/features/docker-in-docker:2": {
32 | "dockerDashComposeVersion": "v2"
33 | },
34 | "ghcr.io/devcontainers/features/common-utils:2": {
35 | "configureZshAsDefaultShell": true,
36 | "username": "root"
37 | },
38 | "ghcr.io/devcontainers-contrib/features/starship:1": {},
39 | "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
40 | "packages": "exa,bat,curl",
41 | "upgradePackages": true
42 | }
43 | },
44 | "customizations": {
45 | "vscode": {
46 | "extensions": [
47 | "GitHub.vscode-pull-request-github",
48 | "jasonlhy.hungry-delete",
49 | "formulahendry.auto-rename-tag",
50 | "DavidAnson.vscode-markdownlint",
51 | "aaron-bond.better-comments",
52 | "streetsidesoftware.code-spell-checker",
53 | "PKief.material-icon-theme",
54 | "ms-azuretools.vscode-docker",
55 | "shardulm94.trailing-spaces",
56 | "tamasfe.even-better-toml",
57 | "GitHub.copilot",
58 | "GitHub.vscode-github-actions",
59 | "GitHub.copilot-chat",
60 | "golang.Go",
61 | "usernamehw.errorlens",
62 | "redhat.vscode-yaml"
63 | ],
64 | "settings": {
65 | "files.eol": "\n",
66 | "remote.extensionKind": {
67 | "ms-azuretools.vscode-docker": "workspace"
68 | },
69 | "go.toolsManagement.checkForUpdates": "local",
70 | "go.toolsManagement.autoUpdate": true,
71 | "go.gopath": "/go",
72 | "go.goroot": "/usr/local/go",
73 | "go.useLanguageServer": true,
74 | "[go]": {
75 | "editor.defaultFormatter": "golang.go",
76 | "editor.codeActionsOnSave": {
77 | "source.organizeImports": true
78 | }
79 | },
80 | "[go.mod]": {
81 | "editor.codeActionsOnSave": {
82 | "source.organizeImports": true
83 | }
84 | },
85 | "gopls": {
86 | "build.buildFlags": [
87 | "-tags",
88 | ""
89 | ],
90 | "formatting.gofumpt": true,
91 | "ui.completion.usePlaceholders": false,
92 | "ui.diagnostic.staticcheck": true,
93 | "ui.semanticTokens": true
94 | },
95 | "go.lintTool": "golangci-lint",
96 | "go.lintOnSave": "package",
97 | "go.testFlags": [
98 | "-v",
99 | "-race"
100 | ],
101 | "go.testTimeout": "30s",
102 | "go.coverOnSingleTest": true,
103 | "go.coverOnSingleTestFile": true,
104 | "go.coverOnTestPackage": true,
105 | "editor.formatOnSave": true,
106 | "editor.formatOnPaste": true,
107 | "editor.bracketPairColorization.enabled": true,
108 | "editor.guides.bracketPairs": "active",
109 | "workbench.iconTheme": "material-icon-theme",
110 | "editor.fontFamily": "'Fira Code', Menlo, Monaco, 'Courier New', monospace",
111 | "editor.fontLigatures": true,
112 | "files.insertFinalNewline": true,
113 | "files.trimFinalNewlines": true
114 | }
115 | }
116 | },
117 | "portsAttributes": {
118 | "9000": {
119 | "label": "Hello Remote World",
120 | "onAutoForward": "notify"
121 | }
122 | },
123 | "postCreateCommand": {
124 | "install-zsh-plugins": "git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting && git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions",
125 | "modules": "go mod download",
126 | "taskfile": "go install github.com/go-task/task/v3/cmd/task@latest",
127 | "gomarkdoc": "go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest",
128 | "goreleaser": "go install github.com/goreleaser/goreleaser/v2@latest"
129 | },
130 | "remoteUser": "root",
131 | "containerUser": "root",
132 | "containerEnv": {
133 | "MOLLIE_API_TOKEN": "${localEnv:MOLLIE_API_TOKEN}",
134 | "MOLLIE_ORG_TOKEN": "${localEnv:MOLLIE_ORG_TOKEN}"
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | ## Project files and directories
2 |
3 | .git
4 | .github
5 | docs
6 | examples
7 | LICENSE
8 | .scrutinizer.yml
9 | .goreleaser.yml
10 | .gitignore
11 | .dockerignore
12 | Dockerfile
13 | Makefile
14 | *.md
15 |
16 | # Binaries for programs and plugins
17 |
18 | *.exe
19 | *.exe~
20 | *.dll
21 | *.so
22 | *.dylib
23 |
24 | # Test binary, build with `go test -c`
25 |
26 | *.test
27 |
28 | # Output of the go coverage tool, specifically when used with LiteIDE
29 |
30 | *.out
31 |
32 | # Visual Studio Code
33 |
34 | .vscode
35 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to make participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at `deltatuts@gmail.com`. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are **welcome** and will be fully **credited**.
4 |
5 | We accept contributions via Pull Requests on [Github](https://github.com/victoravelar/mollie-api-go).
6 |
7 | ## Before starting to code
8 |
9 | If you are contributing based on an issue, please drop a comment in the issue so that it can be labeled as `in progress` and assigned to you, this provides everyone visiting the repository with a clear overview of what is up for grabs and what is already taken.
10 |
11 | Also be sure to ask and clarify all your questions so that your coding experience is as smooth as possible.
12 |
13 | If your PR is not based on an issue, then please provide a relevant and descriptive PR description and branch name.
14 |
15 | ## Pull Requests
16 |
17 | - **Passes code style checks from scrutinizer**
18 |
19 | - **Add tests!** - Your code won't be accepted if it doesn't have tests.
20 |
21 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
22 |
23 | - **Create feature branches** - Don't ask us to pull from your master branch.
24 |
25 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
26 |
27 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
28 |
29 |
30 | ## Running Tests
31 |
32 | ``` bash
33 | $ go test -v -race ./...
34 | ```
35 |
36 | **Happy coding**!
37 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | custom: ['paypal.me/avelarossorio']
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', 'help-wanted']
6 | assignees: 'VictorAvelar'
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 |
16 | 1. Go to '...'
17 | 2. Click on '....'
18 | 3. Scroll down to '....'
19 | 4. See error
20 |
21 | **Expected behavior**
22 | A clear and concise description of what you expected to happen.
23 |
24 | **Screenshots**
25 | If applicable, add screenshots to help explain your problem.
26 |
27 | **Desktop (please complete the following information):**
28 |
29 | - OS: [e.g. iOS]
30 | - Browser [e.g. chrome, safari]
31 | - Version [e.g. 22]
32 |
33 | **Smartphone (please complete the following information):**
34 |
35 | - Device: [e.g. iPhone6]
36 | - OS: [e.g. iOS8.1]
37 | - Browser [e.g. stock browser, safari]
38 | - Version [e.g. 22]
39 |
40 | **Additional context**
41 | Add any other context about the problem here.
42 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: true
2 | contact_links:
3 | - name: Victor H. Avelar
4 | url: https://victoravelar.com
5 | about: Maintainer
6 | - name: Mollie's API Documentation
7 | url: https://docs.mollie.com/index
8 | about: Official API Documentation
9 | - name: Mollie's Changelog
10 | url: https://docs.mollie.com/changelog/v2/changelog
11 | about: Changes announced in the API
12 |
--------------------------------------------------------------------------------
/.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 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/housekeeping.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Repository housekeeping
3 | about: A regular code check for improvements or tech debt
4 | title: 'Library housekeeping - [PERIOD]'
5 | labels: 'housekeeping'
6 | assignees: 'VictorAvelar'
7 |
8 | ---
9 |
10 | ## List of changes
11 |
12 | - General change
13 | - Addition to x
14 |
15 | **External references**
16 | Links or additional resources to provide context to the introduced changes
17 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Description
4 |
5 | Describe your changes in detail.
6 |
7 | ## Motivation and context
8 |
9 | Why is this change required? What problem does it solve?
10 |
11 | If it fixes an open issue, please link to the issue here (if you write `fixes #num`
12 | or `closes #num`, the issue will be automatically closed when the pull is accepted.)
13 |
14 | ## How has this been tested?
15 |
16 | Please describe in detail how you tested your changes.
17 |
18 | Include details of your testing environment, and the tests you ran to
19 | see how your change affects other areas of the code, etc.
20 |
21 | - [ ] Unit tests added / updated
22 | - [ ] The tests run on docker (using `make test`)
23 | - [ ] The required `test data` has been added / updated
24 |
25 | If you are running the tests locally, please specify:
26 |
27 | - OS: [e.g. ubuntu/macos/windows]
28 | - Go version [e.g. 1.19.x]
29 |
30 | ## Types of changes
31 |
32 | What types of changes does your code introduce? Put an `x` in all the boxes that apply:
33 |
34 | - [ ] Bug fix (non-breaking change which fixes an issue)
35 | - [ ] New feature (non-breaking change which adds functionality)
36 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
37 | - [ ] This change requires a documentation update
38 |
39 | ## Checklist
40 |
41 | Go over all the following points, and put an `x` in all the boxes that apply.
42 |
43 | Please, please, please, don't send your pull request until all of the boxes are ticked. Once your pull request is created, it will trigger a build on our continuous integration server to make sure your [tests and code style pass](https://help.github.com/articles/about-required-status-checks/).
44 |
45 | - [ ] I have read the **[CONTRIBUTING](CONTRIBUTING.md)** document.
46 | - [ ] My pull request addresses exactly one patch/feature.
47 | - [ ] I have created a branch for this patch/feature.
48 | - [ ] Each individual commit in the pull request is meaningful.
49 | - [ ] If my change requires a change to the documentation, I have updated it accordingly.
50 |
51 |
52 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: gomod
4 | directory: "/"
5 | schedule:
6 | interval: weekly
7 | time: "04:00"
8 | open-pull-requests-limit: 10
9 | reviewers:
10 | - VictorAvelar
11 |
12 | - package-ecosystem: github-actions
13 | directory: '/'
14 | schedule:
15 | interval: daily
16 | target-branch: master
17 | reviewers:
18 | - VictorAvelar
19 |
20 | - package-ecosystem: docker
21 | directory: '/'
22 | schedule:
23 | interval: weekly
24 | target-branch: master
25 | reviewers:
26 | - VictorAvelar
27 |
28 | - package-ecosystem: docker
29 | directory: '.devcontainer'
30 | schedule:
31 | interval: weekly
32 | target-branch: master
33 | reviewers:
34 | - VictorAvelar
35 |
--------------------------------------------------------------------------------
/.github/labeler.yml:
--------------------------------------------------------------------------------
1 | tests:
2 | - changed-files:
3 | - any-glob-to-any-file: ["mollie/*_test.go", "pkg/**/*_test.go"]
4 |
5 | documentation:
6 | - changed-files:
7 | - any-glob-to-any-file: ["docs/*"]
8 |
9 | docker:
10 | - changed-files:
11 | - any-glob-to-any-file: ["Dockerfile", ".dockerignore"]
12 |
13 | devcontainers:
14 | - changed-files:
15 | - any-glob-to-any-file: [".devcontainer/*"]
16 |
17 | gh-actions:
18 | - changed-files:
19 | - any-glob-to-any-file: [".github/**/*.yml"]
20 |
21 | core:
22 | - changed-files:
23 | - any-glob-to-any-file:
24 | ["mollie/*.go", "!mollie/*_tests.go", "!mollie/tools/*"]
25 |
26 | modules:
27 | - changed-files:
28 | - any-glob-to-any-file: ["go.*"]
29 |
30 | testdata:
31 | - changed-files:
32 | - any-glob-to-any-file: ["testdata/*"]
33 |
34 | config:
35 | - changed-files:
36 | - any-glob-to-any-file: [".*.yml"]
37 |
38 | meta-files:
39 | - changed-files:
40 | - any-glob-to-any-file:
41 | ["Taskfile", "README.md", "LICENSE", "SECURITY.md"]
42 |
43 | pkg:
44 | - changed-files:
45 | - any-glob-to-any-file: ["mollie/pkg/**/*.go"]
46 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 5
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 10
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | - question
10 | - request-info
11 | - documentation
12 | - investigation
13 | # Label to use when marking an issue as stale
14 | staleLabel: stale
15 | # Comment to post when marking an issue as stale. Set to `false` to disable
16 | markComment: >
17 | This issue has been automatically marked as stale because it has not had
18 | recent activity. It will be closed if no further activity occurs. Thank you
19 | for your contributions.
20 | # Comment to post when closing a stale issue. Set to `false` to disable
21 | closeComment: >
22 | This issue has been automatically closed because it has not had
23 | any activity after being labeled as staled.
24 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | paths-ignore:
7 | - "**.md"
8 | - "*.yml"
9 | - "Makefile"
10 | - "docs/*.md"
11 | - ".gitignore"
12 | - "LICENSE"
13 | - ".github/*.yml"
14 | - ".github/ISSUE_TEMPLATE/*.md"
15 | - ".github/*.md"
16 | - ".github/workflows/main.yml"
17 | - ".github/workflows/release.yml"
18 | - ".devcontainers/**"
19 | schedule:
20 | - cron: "21 11 * * 4"
21 |
22 | jobs:
23 | analyze:
24 | name: Analyze
25 | runs-on: ubuntu-latest
26 | permissions:
27 | actions: read
28 | contents: read
29 | security-events: write
30 |
31 | strategy:
32 | fail-fast: false
33 | matrix:
34 | language: ["go"]
35 |
36 | steps:
37 | - name: Checkout repository
38 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
39 |
40 | # Initializes the CodeQL tools for scanning.
41 | - name: Initialize CodeQL
42 | uses: github/codeql-action/init@81644f35ff20aa6b0e7b936f0e8716419ba7d295
43 | with:
44 | languages: 'go'
45 | - name: Autobuild
46 | uses: github/codeql-action/autobuild@81644f35ff20aa6b0e7b936f0e8716419ba7d295
47 |
48 | - name: Perform CodeQL Analysis
49 | uses: github/codeql-action/analyze@81644f35ff20aa6b0e7b936f0e8716419ba7d295
50 |
--------------------------------------------------------------------------------
/.github/workflows/documentation-links.yml:
--------------------------------------------------------------------------------
1 | on:
2 | workflow_dispatch:
3 | push:
4 | branches:
5 | - master
6 | paths:
7 | - "docs/**"
8 | - ".github/workflows/documentation-links.yml"
9 | pull_request:
10 | paths:
11 | - "docs/**"
12 | - ".github/workflows/documentation-links.yml"
13 |
14 | jobs:
15 | linkChecker:
16 | if: github.actor != 'dependabot[bot]'
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
20 |
21 | - name: Restore lychee cache
22 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
23 | with:
24 | path: .lycheecache
25 | key: cache-lychee-${{ github.ref }}
26 | restore-keys: cache-lychee-
27 |
28 | - name: Link Checker
29 | id: lychee
30 | uses: lycheeverse/lychee-action@82202e5e9c2f4ef1a55a3d02563e1cb6041e5332 # v2.4.1
31 | with:
32 | args: "--base . --cache --max-cache-age 1d ."
33 | output: ./lychee/out.md
34 | fail: false
35 |
36 | - name: 'Look for an existing issue'
37 | id: last-issue
38 | uses: micalevisk/last-issue-action@044e1cb7e9a4dde20e22969cb67818bfca0797be # v2.0.0
39 | with:
40 | state: open
41 | labels: lychee
42 |
43 | - name: Create Issue From File
44 | if: steps.lychee.outputs.exit_code != 0
45 | uses: peter-evans/create-issue-from-file@e8ef132d6df98ed982188e460ebb3b5d4ef3a9cd # 5.0.1
46 | with:
47 | title: Link Checker Report
48 | issue-number: ${{ steps.last-issue.outputs.issue_number }}
49 | content-filepath: ./lychee/out.md
50 | labels: report, automated-issue, lychee
51 | assignees: VictorAvelar
52 |
--------------------------------------------------------------------------------
/.github/workflows/labeler.yml:
--------------------------------------------------------------------------------
1 | name: "PR label assigner"
2 |
3 | on:
4 | workflow_dispatch:
5 | pull_request:
6 | branches:
7 | - master
8 | pull_request_target:
9 | branches:
10 | - master
11 |
12 | jobs:
13 | triage:
14 | permissions:
15 | checks: write
16 | contents: read
17 | pull-requests: write
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9
21 | with:
22 | repo-token: "${{ secrets.GITHUB_TOKEN }}"
23 | sync-labels: true
24 |
--------------------------------------------------------------------------------
/.github/workflows/linter.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - master
5 | paths-ignore:
6 | - "**.md"
7 | - "*.yml"
8 | - "Makefile"
9 | - "docs/*.md"
10 | - ".gitignore"
11 | - "LICENSE"
12 | - ".github/*.yml"
13 | - ".github/ISSUE_TEMPLATE/*.md"
14 | - ".github/*.md"
15 | - ".github/workflows/main.yml"
16 | - ".github/workflows/release.yml"
17 | pull_request:
18 | paths-ignore:
19 | - "**.md"
20 | - "*.yml"
21 | - "Makefile"
22 | - "docs/*.md"
23 | - ".gitignore"
24 | - "LICENSE"
25 | - ".github/*.yml"
26 | - ".github/ISSUE_TEMPLATE/*.md"
27 | - ".github/*.md"
28 | - ".github/workflows/main.yml"
29 | - ".github/workflows/release.yml"
30 | jobs:
31 | golangci:
32 | name: linter
33 | runs-on: ubuntu-latest
34 | permissions:
35 | contents: read
36 | pull-requests: read
37 | checks: write
38 | steps:
39 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
40 | with:
41 | fetch-depth: '0'
42 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a
43 | with:
44 | go-version: 1.x
45 | - uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd
46 | with:
47 | version: latest
48 | install-mode: binary
49 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: goreleaser
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | tags:
7 | - "*"
8 |
9 | jobs:
10 | goreleaser:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
14 | with:
15 | fetch-depth: 0
16 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a
17 | with:
18 | go-version: 1.23.x
19 | - uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf
20 | with:
21 | distribution: goreleaser
22 | version: latest
23 | args: release --clean
24 | env:
25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | concurrency:
2 | group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }}
3 | cancel-in-progress: true
4 |
5 | name: testing
6 |
7 | permissions:
8 | contents: read
9 |
10 | on:
11 | push:
12 | branches:
13 | - master
14 | paths-ignore:
15 | - "**.md"
16 | - "*.yml"
17 | - "Makefile"
18 | - "docs/*.md"
19 | - ".gitignore"
20 | - "LICENSE"
21 | - ".github/*.yml"
22 | - ".github/ISSUE_TEMPLATE/*.md"
23 | - ".github/*.md"
24 | - ".github/workflows/release.yml"
25 | pull_request:
26 | paths-ignore:
27 | - "**.md"
28 | - "*.yml"
29 | - "Makefile"
30 | - "docs/*.md"
31 | - ".gitignore"
32 | - "LICENSE"
33 | - ".github/*.yml"
34 | - ".github/ISSUE_TEMPLATE/*.md"
35 | - ".github/*.md"
36 | - ".github/workflows/release.yml"
37 | jobs:
38 | tests:
39 | permissions:
40 | contents: read
41 | strategy:
42 | matrix:
43 | go: [1.x, 1.23.x]
44 | runs-on: ubuntu-latest
45 | steps:
46 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
47 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # 5.2.0
48 | with:
49 | go-version: ${{ matrix.go }}
50 | # Get values for cache paths to be used in later steps
51 | - id: cache-paths
52 | run: |
53 | echo "go-cache=$(go env GOCACHE)" >> $GITHUB_OUTPUT
54 | echo "go-mod-cache=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT
55 | - name: Cache go modules
56 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
57 | with:
58 | path: |
59 | ${{ steps.cache-paths.outputs.go-cache }}
60 | ${{ steps.cache-paths.outputs.go-mod-cache }}
61 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
62 | restore-keys: ${{ runner.os }}-go-
63 | - run: go test -failfast -timeout 5m ./...
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Visual Studio Code
15 | .vscode
16 |
17 | # Wiki pages
18 | wiki
19 |
20 | dist/
21 |
--------------------------------------------------------------------------------
/.golangci.yaml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | run:
3 | concurrency: 4
4 | go: "1.23"
5 | modules-download-mode: readonly
6 | issues-exit-code: 2
7 | tests: false
8 | output:
9 | formats:
10 | text:
11 | path: stdout
12 | tab:
13 | path: stdout
14 | colors: false
15 | linters:
16 | enable:
17 | - asciicheck
18 | - bidichk
19 | - copyloopvar
20 | - cyclop
21 | - decorder
22 | - depguard
23 | - dogsled
24 | - dupl
25 | - dupword
26 | - funlen
27 | - gocheckcompilerdirectives
28 | - gochecknoinits
29 | - gocognit
30 | - goconst
31 | - gocyclo
32 | - godot
33 | - godox
34 | - goheader
35 | - gomoddirectives
36 | - gomodguard
37 | - goprintffuncname
38 | - grouper
39 | - inamedparam
40 | - interfacebloat
41 | - lll
42 | - maintidx
43 | - misspell
44 | - mnd
45 | - nakedret
46 | - nestif
47 | - nlreturn
48 | - nolintlint
49 | - nosprintfhostport
50 | - prealloc
51 | - predeclared
52 | - promlinter
53 | - tagalign
54 | - testpackage
55 | - usestdlibvars
56 | - whitespace
57 | - wsl
58 | disable:
59 | - tagliatelle
60 | - unused
61 | settings:
62 | depguard:
63 | rules:
64 | main:
65 | files:
66 | - $all
67 | allow:
68 | - $gostd
69 | - github.com/google
70 | - golang.org/x/oauth2
71 | - github.com/VictorAvelar/mollie-api-go/v4/
72 | tests:
73 | files:
74 | - $test
75 | allow:
76 | - github.com/stretchr/testify
77 | exclusions:
78 | generated: lax
79 | presets:
80 | - comments
81 | - common-false-positives
82 | - legacy
83 | - std-error-handling
84 | rules:
85 | - linters:
86 | - lll
87 | source: BusinessCategory
88 | paths:
89 | - third_party$
90 | - builtin$
91 | - examples$
92 | issues:
93 | new: true
94 | fix: true
95 | formatters:
96 | enable:
97 | - gci
98 | - gofmt
99 | - gofumpt
100 | - goimports
101 | exclusions:
102 | generated: lax
103 | paths:
104 | - third_party$
105 | - builtin$
106 | - examples$
107 |
--------------------------------------------------------------------------------
/.goreleaser.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | builds:
4 | - skip: true
5 |
6 | changelog:
7 | use: github
8 | sort: asc
9 | groups:
10 | - title: "New Features and updates"
11 | regexp: "^.*feat[(\\w)]*:+.*$"
12 | order: 0
13 | - title: "Bug fixes"
14 | regexp: "^.*fix[(\\w)]*:+.*$"
15 | order: 10
16 | - title: "Documentation updates"
17 | regexp: "^.*docs[(\\w)]*:+.*$"
18 | order: 20
19 | - title: Other work
20 | order: 999
21 | filters:
22 | exclude:
23 | - ^test
24 | - changelog
25 | - typo
26 | - Readme
27 | - ^Merge pull request
28 | - ^Merge branch
29 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @VictorAvelar
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.24.4-alpine
2 |
3 | ENV CGO_ENABLED=0
4 |
5 | WORKDIR /app
6 |
7 | COPY go.mod .
8 | COPY go.sum .
9 |
10 | RUN go mod download
11 |
12 | COPY . .
13 |
14 | ENTRYPOINT ["go", "test", "-v", "./mollie/...", "-coverprofile", "cover.out"]
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Victor Hugo Avelar Ossorio
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mollie API Golang client
2 |
3 | ## Deepwiki
4 |
5 | [](https://deepwiki.com/VictorAvelar/mollie-api-go)
6 |
7 | ## Actions
8 |
9 | [](https://github.com/VictorAvelar/mollie-api-go/actions/workflows/tests.yml)
10 | [](https://github.com/VictorAvelar/mollie-api-go/actions/workflows/linter.yml)
11 | [](https://github.com/VictorAvelar/mollie-api-go/actions/workflows/codeql-analysis.yml)
12 |
13 | ## Go ecosystem
14 |
15 | [](https://pkg.go.dev/github.com/VictorAvelar/mollie-api-go/v4/mollie)
16 | [](https://goreportcard.com/report/github.com/VictorAvelar/mollie-api-go/v3)
17 | [](https://bestpractices.coreinfrastructure.org/projects/3522)
18 |
19 | Accepting [iDEAL](https://www.mollie.com/payments/ideal/), [Apple Pay](https://www.mollie.com/payments/apple-pay), [Bancontact](https://www.mollie.com/payments/bancontact/), [SOFORT Banking](https://www.mollie.com/payments/sofort/), [Creditcard](https://www.mollie.com/payments/credit-card/), [SEPA Bank transfer](https://www.mollie.com/payments/bank-transfer/), [SEPA Direct debit](https://www.mollie.com/payments/direct-debit/), [PayPal](https://www.mollie.com/payments/paypal/), [Belfius Direct Net](https://www.mollie.com/payments/belfius/), [KBC/CBC](https://www.mollie.com/payments/kbc-cbc/), [paysafecard](https://www.mollie.com/payments/paysafecard/), [Giftcards](https://www.mollie.com/payments/gift-cards/), [EPS](https://www.mollie.com/payments/eps/) and [Przelewy24](https://www.mollie.com/payments/przelewy24/) online payments without fixed monthly costs or any punishing registration procedures. Just use the Mollie API to receive payments directly on your website or easily refund transactions to your customers.
20 |
21 | ## Requirements
22 |
23 | To use the Mollie API client, the following things are required:
24 |
25 | - Get yourself a free [Mollie account](https://www.mollie.com/signup). No sign up costs.
26 | - Now you're ready to use the Mollie API client in test mode.
27 | - Follow [a few steps](https://www.mollie.com/dashboard/?modal=onboarding) to enable payment methods in live mode, and let us handle the rest.
28 | - Up-to-date OpenSSL (or other SSL/TLS toolkit)
29 |
30 | For leveraging [Mollie Connect](https://docs.mollie.com/oauth/overview) (advanced use cases only), it is recommended to be familiar with the OAuth2 protocol.
31 |
32 | ## Install
33 |
34 | ```sh
35 | go get -u github.com/VictorAvelar/mollie-api-go/v4/mollie
36 | ```
37 |
38 | ## Notice
39 |
40 | > Version 4.6.0 raises the minimum go version to v1.23, patches will likely be backported to v4.5.x but new features will only be available in versions > v4.6.0.
41 |
42 | The above notice aligns with the go [EOL](https://endoflife.date/go) policy schedule.
43 |
44 | ## Usage
45 |
46 | ### Testing using API tokens
47 |
48 | #### Using the config helper
49 |
50 | ```go
51 | // Create a configuration object with idempotency enabled.
52 | config := mollie.NewAPITestingConfig(true)
53 | ```
54 |
55 | #### Using the NewConfig method
56 |
57 | ```go
58 | // Create a configuration object with idempotency enabled.
59 | config := mollie.NewConfig(true, mollie.ApiTokenEnv)
60 |
61 | _ := config.ToggleIdempotency()
62 | ```
63 |
64 | ### Testing using Organization access tokens
65 |
66 | #### Using the config helper for org tokens
67 |
68 | ```go
69 | // Create a configuration object with idempotency enabled.
70 | config := mollie.NewOrgTestingConfig(true)
71 | ```
72 |
73 | #### Using the NewConfig method for org tokens
74 |
75 | ```go
76 | // Create a configuration object with idempotency enabled.
77 | config := mollie.NewConfig(true, mollie.OrgTokenEnv)
78 |
79 | _ := config.ToggleIdempotency()
80 | ```
81 |
82 | ### Create an API client
83 |
84 | ```go
85 | // build your desired config
86 | client, err := mollie.NewClient(config)
87 | if err != nil {
88 | log.Fatal(err)
89 | }
90 | // perform operations with the API.
91 | ```
92 |
93 | ## Upgrade guide
94 |
95 | - If you want to upgrade from v2 -> v3, the list of breaking and notable changes can be found in the [docs](docs/v3-upgrade.md).
96 | - If you want to upgrade from v3 -> v4, the list of breaking and notable changes can be found in the [docs](docs/v4-upgrade.md).
97 |
98 | ## API parity
99 |
100 | Checks to the API changelog are performed constantly to ensure API parity and compatibility, however it might happen that not all the changes are implemented right away.
101 |
102 | For checking all the related tasks you can check the issues labeled with the [API parity](https://github.com/VictorAvelar/mollie-api-go/labels/API%20parity) label.
103 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Even though go offers full backwards compatibility, this package will only be concerned about the versions tested and
6 | declared inside the `.github/workflows/main.yml` file.
7 |
8 | | Version | Supported |
9 | | ------- | ------------------ |
10 | | 1.13.x | :warning: |
11 | | 1.14.x | :warning: |
12 | | 1.15.x | :warning: |
13 | | 1.16.x | :warning: |
14 | | 1.17.x | :warning: |
15 | | 1.18.x | :warning: |
16 | | 1.19.x | :warning: |
17 | | 1.20.x | :white_check_mark: |
18 | | 1.21.x | :white_check_mark: |
19 | | 1.22.x | :white_check_mark: |
20 | | 1.23.x | :white_check_mark: |
21 | | master | :x: |
22 |
23 | ## Reporting a Vulnerability
24 |
25 | For a vulnerability in a dependency or third party package, please create an issue and also be sure to report the problem in the repository
26 | were the dependency is developed.
27 |
28 | For a vulnerability issue detected on this repository, please send an email to [deltatuts@gmail.com](mailto:deltatuts@gmail.com)
29 |
--------------------------------------------------------------------------------
/Taskfile.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 |
3 | tasks:
4 | build:
5 | desc: "Build the project using the Dockerfile go installation"
6 | cmds:
7 | - docker build -t mollie-go:latest -f Dockerfile .
8 | silent: true
9 |
10 | run:
11 | desc: "Run the tests using the Dockerfile go installation"
12 | cmds:
13 | - docker run --rm mollie-go:latest
14 | silent: true
15 |
16 | ci-lint:
17 | desc: "Run all linters used in the CI pipeline"
18 | cmds:
19 | - golangci-lint run --output.tab.path stdout -n --uniq-by-line
20 | silent: true
21 |
22 | ci-lint-all:
23 | desc: "Run all linters used in the CI pipeline"
24 | cmds:
25 | - golangci-lint run --output.tab.path stdout --uniq-by-line
26 | silent: true
27 |
28 | lint:
29 | desc: "Run the go linters"
30 | cmds:
31 | - go version
32 | - echo "Running go lint"
33 | - golint ./...
34 | - echo "Running go vet"
35 | - go vet ./...
36 | silent: false
37 |
38 | test:
39 | desc: "Run tests using the Dockerfile go installation"
40 | cmds:
41 | - task: run
42 | silent: false
43 |
44 | test-local:
45 | desc: "Run tests using a local go installation"
46 | cmds:
47 | - go test -failfast ./... -coverprofile cover.out
48 | silent: false
49 |
50 | coverage:
51 | desc: "Run tests and generate a default coverage report"
52 | cmds:
53 | - go tool cover -func=cover.out
54 | silent: false
55 |
56 | cover-report:
57 | desc: "Run tests and generate coverage report in HTML format"
58 | cmds:
59 | - go tool cover -html=cover.out
60 | silent: false
61 |
62 | clean:
63 | desc: "Verify and tidy go modules"
64 | cmds:
65 | - go mod verify
66 | - go mod tidy
67 | silent: false
68 |
69 | update-docs:
70 | desc: "Update the generated docs for the mollie package"
71 | cmds:
72 | - gomarkdoc --output docs/README.md ./mollie
73 | - git add --all
74 | - "git commit --message 'chore(docs): update generated docs'"
75 | silent: false
76 |
77 | sub-pkg-docs:
78 | desc: "Update the generated docs for the sub-packages"
79 | cmds:
80 | - gomarkdoc ./pkg/connect > docs/pkg/connect/README.md
81 | - gomarkdoc ./pkg/idempotency > docs/pkg/idempotency/README.md
82 | - gomarkdoc ./pkg/pagination > docs/pkg/pagination/README.md
83 | silent: false
84 |
--------------------------------------------------------------------------------
/docs/pkg/connect/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # connect
4 |
5 | ```go
6 | import "github.com/VictorAvelar/mollie-api-go/v4/pkg/connect"
7 | ```
8 |
9 | ## Index
10 |
11 | - [func OauthEndpoint\(\) \*oauth2.Endpoint](<#OauthEndpoint>)
12 |
13 |
14 |
15 | ## func [OauthEndpoint]()
16 |
17 | ```go
18 | func OauthEndpoint() *oauth2.Endpoint
19 | ```
20 |
21 | OauthEndpoint is Mollies's OAuth 2.0 endpoint.
22 |
23 | Generated by [gomarkdoc]()
24 |
--------------------------------------------------------------------------------
/docs/pkg/idempotency/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # idempotency
4 |
5 | ```go
6 | import "github.com/VictorAvelar/mollie-api-go/v4/pkg/idempotency"
7 | ```
8 |
9 | Package idempotency contains the services in charge of generating a unique keys to be passed in POST requests to ensure operation uniqueness.
10 |
11 | See: https://docs.mollie.com/overview/api-idempotency
12 |
13 | The std generator uses google's uuid library to return a new uuid as unique idempotency key.
14 |
15 | You can build your own generator and pass it to the library by implementing the KeyGenerator interface.
16 |
17 | ## Index
18 |
19 | - [Constants](<#constants>)
20 | - [type KeyGenerator](<#KeyGenerator>)
21 | - [func NewNopGenerator\(exp string\) KeyGenerator](<#NewNopGenerator>)
22 | - [func NewStdGenerator\(\) KeyGenerator](<#NewStdGenerator>)
23 |
24 |
25 | ## Constants
26 |
27 | TestKeyExpected is the default value for the NOpGenerator.
28 |
29 | ```go
30 | const (
31 | TestKeyExpected = "test_ikg_key"
32 | )
33 | ```
34 |
35 |
36 | ## type [KeyGenerator]()
37 |
38 | KeyGenerator describes the service in charge of generating a unique idempotency key to be passed in POST requests to ensure operation uniqueness.
39 |
40 | See: https://docs.mollie.com/overview/api-idempotency
41 |
42 | ```go
43 | type KeyGenerator interface {
44 | // Generate encapsulates the logic to return a string representation of
45 | // a unique idempotency key.
46 | Generate() string
47 | }
48 | ```
49 |
50 |
51 | ### func [NewNopGenerator]()
52 |
53 | ```go
54 | func NewNopGenerator(exp string) KeyGenerator
55 | ```
56 |
57 | NewNopGenerator returns a dummy implementation of the IdempotencyKeyGenerator interface.
58 |
59 | Good for testing or when a predictable result is required.
60 |
61 | If exp is an empty string, then TestKeyExpected is used as default value for the NOpGenerator.
62 |
63 |
64 | ### func [NewStdGenerator]()
65 |
66 | ```go
67 | func NewStdGenerator() KeyGenerator
68 | ```
69 |
70 | NewStdGenerator returns an standard and common way of generating idempotency unique keys.
71 |
72 | Generated by [gomarkdoc]()
73 |
--------------------------------------------------------------------------------
/docs/pkg/pagination/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # pagination
4 |
5 | ```go
6 | import "github.com/VictorAvelar/mollie-api-go/v4/pkg/pagination"
7 | ```
8 |
9 | Package pagination provides utilities to handle pagination in API responses.
10 |
11 | Pagination is a common feature in APIs that allows retrieval of large datasets in smaller chunks, enhancing performance and resource usage. This package aims to simplify pagination\-related tasks by providing helpful functions.
12 |
13 | ## Index
14 |
15 | - [func ExtractFromQueryParam\(uri string\) \(lastID string, err error\)](<#ExtractFromQueryParam>)
16 |
17 |
18 |
19 | ## func [ExtractFromQueryParam]()
20 |
21 | ```go
22 | func ExtractFromQueryParam(uri string) (lastID string, err error)
23 | ```
24 |
25 | ExtractFromQueryParam extracts the lastID from the given URI, which is assumed to be a URL with query parameters. It specifically looks for a query parameter named 'from' and returns its value as a string. If the URI cannot be parsed or the query parameter is not found, it returns an empty string and the encountered error.
26 |
27 | Generated by [gomarkdoc]()
28 |
--------------------------------------------------------------------------------
/docs/v3-upgrade.md:
--------------------------------------------------------------------------------
1 | # Changes included in v3
2 |
3 | ## Breaking changes
4 |
5 | - Minimum go version is now v1.17
6 | - All service actions accept a `context.Context` as first parameter.
7 | - Methods service actions now return pointers instead of struct literals.
8 | - Invoice service actions now return pointers instead of struct literals.
9 | - Mandates service actions now return pointers instead of struct literals.
10 | - Payments service actions now return pointers instead of struct literals.
11 | - Get permissions now accepts a direct reference to a PermissionGrant struct instead of a string.
12 | - All services now return an instance of mollie.Response as first value changing the signature from (value, error) to (response, value, error).
13 | - All data structures are changed to the following naming patterns
14 | - For Lists: {Value}List
15 | - For Options: {Value}Options | {Value}ListOptions
16 | - For Links: {Value}Links | {Value}ListLinks
17 | - Params now contain the full name of the value they contain. Ex. pID now is payment.
18 | - Methods and its references is now PaymentMethods to make its purpose clear.
19 | - Services that now return the full response object as part of the returned values (the response is always the first value of `n` returned):
20 | - Captures
21 | - Chargebacks
22 | - Customers
23 | - Invoices
24 | - Mandates
25 | - Miscellaneous
26 | - Onboarding
27 | - Partners
28 | - Orders
29 | - Organizations
30 | - PaymentLinks
31 | - PaymentMethods
32 | - Payments
33 | - Permissions
34 | - Profiles
35 | - Refunds
36 | - Settlements
37 | - Shipments
38 | - Subscriptions
39 | - Errors now use the `mollie.BaseError` type to provide better error reporting.
40 | - The type `mollie.Error` is removed from the codebase.
41 | - PaginationLinks changed to pointers.
42 |
43 | ## Other changes
44 |
45 | - All the tests are now using testify.Suite
46 | - Removed the example tests as it was not accurate nor well implemented.
47 | - Remove CHANGELOG.md as the releases now provide a more accurate report of changes.
48 | - Some typos were fixed on several query serializable param tags
49 | - Client now contains helpers for the used http actions (get, post, patch & delete) to simplify the way the requests are dispatched.
50 |
--------------------------------------------------------------------------------
/docs/v4-upgrade.md:
--------------------------------------------------------------------------------
1 | # Notable changes included in v4
2 |
3 | ## Breaking changes
4 |
5 | - `idempotency` package has moved from the `mollie` directory to the `pkg` directory.
6 | - `pagination` package has moved from the`mollie` directory to the `pkg` directory.
7 | - `connect` package has moved from the `mollie` directory to the `pkg` directory.
8 | - root namespace is not `github.com/VictorAvelar/mollie-api-go/v4/`.
9 | - Changes in all resources:
10 | - Data structures to describe request content in create, update and get operations are no longer the same, e.g. for `payments` there is a `CreatePayment` struct, a `UpdatePayment` struct and a `Payment` struct. This enables future extensions and modifications without having to check for cross request compatibility.
11 | - Data structures that describe available url parameters are consistently names `List{ResourceName}Options`.
12 | - Data structures that describe lists responses are consistently named as follows: e.g. for payments: `PaymentsList`
13 | - API aliases now use the parent objects, e.g. for settlements when listing payments the options passed to the request are using the `ListPaymentsOptions` object and not a local object.
14 | - All resources were checked for API consistency and parity, optional resources with custom types are now pointers to ensure proper json encoding and decoding to avoid issues as the one mentioned un #271
15 | - All resources embed a struct containing all the fields specific to access tokens, following this pattern the same happens for fields specific to Mollie connect
16 |
17 | ## Other changes
18 |
19 | - `testify.Suite` was removed from all testing.
20 | - Improvements for devcontainer files
21 | - Major versions of multiple github actions updated
22 | - Base `Dockerfile` using Go 1.22.x
23 | - Tests were update to use the new types.
24 | - Test coverage was slightly improved.
25 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/VictorAvelar/mollie-api-go/v4
2 |
3 | go 1.23.0
4 |
5 | require (
6 | github.com/google/go-querystring v1.1.0
7 | github.com/google/uuid v1.6.0
8 | github.com/stretchr/testify v1.10.0
9 | golang.org/x/oauth2 v0.30.0
10 | )
11 |
12 | require (
13 | github.com/davecgh/go-spew v1.1.1 // indirect
14 | github.com/pmezard/go-difflib v1.0.0 // indirect
15 | gopkg.in/yaml.v3 v3.0.1 // indirect
16 | )
17 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
4 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
5 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
6 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
7 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
8 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
9 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
10 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
11 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
12 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
13 | golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
14 | golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
15 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
17 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
18 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
19 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
20 |
--------------------------------------------------------------------------------
/lychee.toml:
--------------------------------------------------------------------------------
1 | exclude_path = [".github", ".devcontainer", "wiki", "mollie", "testdata"]
2 | exclude = ['^https://github\.com', '^https://api\.mollie\.com']
3 | cache = true
4 | max_redirects = 3
5 | require_https = true
6 | timeout = 10
7 | retry_wait_time = 15
8 | verbose = "error"
9 | no_progress = true
10 |
--------------------------------------------------------------------------------
/mollie/app_fee.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | // ApplicationFee allows you to split a payment between a platform and connected merchant accounts.
4 | type ApplicationFee struct {
5 | Amount *Amount `json:"amount,omitempty"`
6 | Description string `json:"description,omitempty"`
7 | }
8 |
--------------------------------------------------------------------------------
/mollie/captures.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "time"
8 | )
9 |
10 | // CaptureMode describes the mode of a capture.
11 | type CaptureMode string
12 |
13 | // CaptureMode possible values.
14 | const (
15 | AutomaticCapture CaptureMode = "automatic"
16 | ManualCapture CaptureMode = "manual"
17 | )
18 |
19 | // CaptureStatus describes the status of a capture.
20 | type CaptureStatus string
21 |
22 | // CaptureStatus possible values.
23 | const (
24 | CaptureStatusPending CaptureStatus = "pending"
25 | CaptureStatusSucceeded CaptureStatus = "succeeded"
26 | CaptureStatusFailed CaptureStatus = "failed"
27 | )
28 |
29 | // CreateCapture describes the payload for creating a capture.
30 | type CreateCapture struct {
31 | Description string `json:"description,omitempty"`
32 | Metadata any `json:"metadata,omitempty"`
33 | Amount *Amount `json:"amount,omitempty"`
34 | CaptureAccessTokenFields
35 | }
36 |
37 | // CaptureAccessTokenFields describes the payload for creating a capture with an access token.
38 | type CaptureAccessTokenFields struct {
39 | Testmode bool `json:"testmode,omitempty"`
40 | }
41 |
42 | // Capture describes a single capture.
43 | // Captures are used for payments that have the authorize-then-capture flow.
44 | type Capture struct {
45 | Resource string `json:"resource,omitempty"`
46 | ID string `json:"id,omitempty"`
47 | Mode Mode `json:"mode,omitempty"`
48 | Amount *Amount `json:"amount,omitempty"`
49 | Status CaptureStatus `json:"status,omitempty"`
50 | SettlementAmount *Amount `json:"settlementAmount,omitempty"`
51 | PaymentID string `json:"paymentId,omitempty"`
52 | ShipmentID string `json:"shipmentId,omitempty"`
53 | SettlementID string `json:"settlementId,omitempty"`
54 | CreatedAt *time.Time `json:"createdAt,omitempty"`
55 | Metadata any `json:"metadata,omitempty"`
56 | Links CaptureLinks `json:"_links,omitempty"`
57 | CaptureAccessTokenFields
58 | }
59 |
60 | // CaptureLinks contains relevant links for a capture object.
61 | type CaptureLinks struct {
62 | Self *URL `json:"self,omitempty"`
63 | Payment *URL `json:"payment,omitempty"`
64 | Shipment *URL `json:"shipment,omitempty"`
65 | Settlement *URL `json:"settlement,omitempty"`
66 | Documentation *URL `json:"documentation,omitempty"`
67 | }
68 |
69 | // CaptureOptions describes the query params available to use when retrieving captures.
70 | //
71 | // See: https://docs.mollie.com/reference/get-capture#embedding-of-related-resources
72 | type CaptureOptions struct {
73 | Embed []EmbedValue `url:"embed,omitempty"`
74 | }
75 |
76 | // CapturesList describes a list of captures.
77 | type CapturesList struct {
78 | Count int `json:"count,omitempty"`
79 | Embedded struct {
80 | Captures []*Capture
81 | } `json:"_embedded,omitempty"`
82 | Links PaginationLinks `json:"_links,omitempty"`
83 | }
84 |
85 | // CapturesService operates over captures resource.
86 | type CapturesService service
87 |
88 | // Get retrieves a single capture by its ID.
89 | // Note the original payment’s ID is needed as well.
90 | //
91 | // See: https://docs.mollie.com/reference/get-capture
92 | func (cs *CapturesService) Get(ctx context.Context, payment, capture string, options *CaptureOptions) (
93 | res *Response,
94 | c *Capture,
95 | err error,
96 | ) {
97 | u := fmt.Sprintf("v2/payments/%s/captures/%s", payment, capture)
98 |
99 | res, err = cs.client.get(ctx, u, options)
100 | if err != nil {
101 | return
102 | }
103 |
104 | if err = json.Unmarshal(res.content, &c); err != nil {
105 | return
106 | }
107 |
108 | return
109 | }
110 |
111 | // Create creates a new capture for a payment.
112 | //
113 | // See: https://docs.mollie.com/reference/create-capture
114 | func (cs *CapturesService) Create(ctx context.Context, payment string, capture CreateCapture) (
115 | res *Response,
116 | c *Capture,
117 | err error,
118 | ) {
119 | u := fmt.Sprintf("v2/payments/%s/captures", payment)
120 |
121 | if cs.client.HasAccessToken() && cs.client.config.testing {
122 | capture.Testmode = true
123 | }
124 |
125 | res, err = cs.client.post(ctx, u, capture, nil)
126 | if err != nil {
127 | return
128 | }
129 |
130 | if err = json.Unmarshal(res.content, &c); err != nil {
131 | return
132 | }
133 |
134 | return
135 | }
136 |
137 | // List retrieves all captures for a certain payment.
138 | //
139 | // See: https://docs.mollie.com/reference/list-captures
140 | func (cs *CapturesService) List(ctx context.Context, payment string, options *CaptureOptions) (
141 | res *Response,
142 | cl *CapturesList,
143 | err error,
144 | ) {
145 | u := fmt.Sprintf("v2/payments/%s/captures", payment)
146 |
147 | res, err = cs.client.get(ctx, u, options)
148 | if err != nil {
149 | return
150 | }
151 |
152 | if err = json.Unmarshal(res.content, &cl); err != nil {
153 | return
154 | }
155 |
156 | return
157 | }
158 |
--------------------------------------------------------------------------------
/mollie/chargebacks.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "time"
8 | )
9 |
10 | // Chargeback describes a forced transaction reversal initiated by the cardholder's bank.
11 | type Chargeback struct {
12 | Resource string `json:"resource,omitempty"`
13 | ID string `json:"id,omitempty"`
14 | PaymentID string `json:"paymentId,omitempty"`
15 | Amount *Amount `json:"amount,omitempty"`
16 | SettlementAmount *Amount `json:"settlementAmount,omitempty"`
17 | Reason *ChargebackReason `json:"reason,omitempty"`
18 | CreatedAt *time.Time `json:"createdAt,omitempty"`
19 | ReversedAt *time.Time `json:"reversedAt,omitempty"`
20 | Links ChargebackLinks `json:"_links,omitempty"`
21 | ChargebackAccessTokenFields
22 | }
23 |
24 | // ChargebackReason describes the reason for the chargeback as given by the bank.
25 | type ChargebackReason struct {
26 | Code string `json:"code,omitempty"`
27 | Description string `json:"description,omitempty"`
28 | }
29 |
30 | // ChargebackAccessTokenFields describes the fields to be used to create a chargeback access token.
31 | type ChargebackAccessTokenFields struct {
32 | ProfileID string `json:"profileId,omitempty"`
33 | Testmode bool `json:"testmode,omitempty"`
34 | }
35 |
36 | // ChargebackLinks describes all the possible links to be returned with
37 | // a chargeback object.
38 | type ChargebackLinks struct {
39 | Self *URL `json:"self,omitempty"`
40 | Payment *URL `json:"payment,omitempty"`
41 | Settlement *URL `json:"settlement,omitempty"`
42 | Documentation *URL `json:"documentation,omitempty"`
43 | }
44 |
45 | // ChargebackOptions describes chargeback endpoint valid query string parameters.
46 | type ChargebackOptions struct {
47 | Include []IncludeValue `url:"include,omitempty"`
48 | Embed []EmbedValue `url:"embed,omitempty"`
49 | }
50 |
51 | // ListChargebacksOptions describes list chargebacks endpoint valid query string parameters.
52 | type ListChargebacksOptions struct {
53 | From string `url:"from,omitempty"`
54 | Limit int `url:"limit,omitempty"`
55 | Include []IncludeValue `url:"include,omitempty"`
56 | Embed []EmbedValue `url:"embed,omitempty"`
57 | ProfileID string `url:"profileId,omitempty"`
58 | }
59 |
60 | // ChargebacksList describes how a list of chargebacks will be retrieved by Mollie.
61 | type ChargebacksList struct {
62 | Count int `json:"count,omitempty"`
63 | Embedded struct {
64 | Chargebacks []*Chargeback
65 | } `json:"_embedded,omitempty"`
66 | Links PaginationLinks `json:"_links,omitempty"`
67 | }
68 |
69 | // ChargebacksService instance operates over chargeback resources.
70 | type ChargebacksService service
71 |
72 | // Get retrieves a single chargeback by its ID.
73 | // Note the original payment’s ID is needed as well.
74 | //
75 | // See: https://docs.mollie.com/reference/get-chargeback
76 | func (cs *ChargebacksService) Get(ctx context.Context, payment, chargeback string, opts *ChargebackOptions) (
77 | res *Response,
78 | p *Chargeback,
79 | err error,
80 | ) {
81 | u := fmt.Sprintf("v2/payments/%s/chargebacks/%s", payment, chargeback)
82 |
83 | res, err = cs.client.get(ctx, u, opts)
84 | if err != nil {
85 | return
86 | }
87 |
88 | if err = json.Unmarshal(res.content, &p); err != nil {
89 | return
90 | }
91 |
92 | return
93 | }
94 |
95 | // List retrieves a list of chargebacks associated with your account/organization.
96 | //
97 | // See: https://docs.mollie.com/reference/list-chargebacks
98 | func (cs *ChargebacksService) List(ctx context.Context, options *ListChargebacksOptions) (
99 | res *Response,
100 | cl *ChargebacksList,
101 | err error,
102 | ) {
103 | return cs.list(ctx, "v2/chargebacks", options)
104 | }
105 |
106 | // ListForPayment retrieves a list of chargebacks associated with a single payment.
107 | //
108 | // See: https://docs.mollie.com/reference/list-chargebacks
109 | func (cs *ChargebacksService) ListForPayment(ctx context.Context, payment string, options *ListChargebacksOptions) (
110 | res *Response,
111 | cl *ChargebacksList,
112 | err error,
113 | ) {
114 | return cs.list(ctx, fmt.Sprintf("v2/payments/%s/chargebacks", payment), options)
115 | }
116 |
117 | // encapsulates the shared list methods logic.
118 | func (cs *ChargebacksService) list(ctx context.Context, uri string, options interface{}) (
119 | res *Response,
120 | cl *ChargebacksList,
121 | err error,
122 | ) {
123 | res, err = cs.client.get(ctx, uri, options)
124 | if err != nil {
125 | return
126 | }
127 |
128 | if err = json.Unmarshal(res.content, &cl); err != nil {
129 | return
130 | }
131 |
132 | return
133 | }
134 |
--------------------------------------------------------------------------------
/mollie/client_links.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 |
8 | "github.com/google/go-querystring/query"
9 | )
10 |
11 | // CreateClientLink contains information to link a new organization to an
12 | // OAuth application.
13 | type CreateClientLink struct {
14 | Owner Owner `json:"owner,omitempty"`
15 | Name string `json:"name,omitempty"`
16 | Address *Address `json:"address,omitempty"`
17 | RegistrationNumber string `json:"registrationNumber,omitempty"`
18 | VATNumber string `json:"vatNumber,omitempty"`
19 | }
20 |
21 | // ClientLinkLinks describes all the possible links to be returned with
22 | // a client links response object.
23 | type ClientLinkLinks struct {
24 | ClientLink *URL `json:"clientLink,omitempty"`
25 | Documentation *URL `json:"documentation,omitempty"`
26 | }
27 |
28 | // ClientLink object with redirect target.
29 | type ClientLink struct {
30 | ID string `json:"id,omitempty"`
31 | Resource string `json:"resource,omitempty"`
32 | Links ClientLinkLinks `json:"_links,omitempty"`
33 | }
34 |
35 | // ClientLinksService interacts with the Client Links API to create
36 | // new organizations for your customers.
37 | type ClientLinksService service
38 |
39 | // Create a client link based on the provided CreateClientLink values.
40 | //
41 | // See: https://docs.mollie.com/reference/create-client-link
42 | func (cls *ClientLinksService) Create(ctx context.Context, cd CreateClientLink) (
43 | res *Response,
44 | cl *ClientLink,
45 | err error,
46 | ) {
47 | res, err = cls.client.post(ctx, "v2/client-links", cd, nil)
48 | if err != nil {
49 | return
50 | }
51 |
52 | if err = json.Unmarshal(res.content, &cl); err != nil {
53 | return
54 | }
55 |
56 | return
57 | }
58 |
59 | // ApprovalPromptAction represents possible actions to be performed
60 | // once the client link is created and redirected to the dashboard.
61 | type ApprovalPromptAction string
62 |
63 | // Possible approval prompt actions.
64 | const (
65 | ForceApproval ApprovalPromptAction = "force"
66 | AutoApproval ApprovalPromptAction = "auto"
67 | )
68 |
69 | // ClientLinkAuthorizeOptions subset of the parameters allowed for the Authorize endpoint.
70 | type ClientLinkAuthorizeOptions struct {
71 | ClientID string `url:"clientId,omitempty"`
72 | State string `url:"state,omitempty"`
73 | Scope []PermissionGrant `del:"+" url:"scope,omitempty"`
74 | ApprovalPrompt ApprovalPromptAction `url:"approvalPrompt,omitempty"`
75 | }
76 |
77 | // GetFinalClientLink returns the final client link URI with the provided options.
78 | func (cls *ClientLinksService) GetFinalClientLink(
79 | ctx context.Context,
80 | clientLink string,
81 | options *ClientLinkAuthorizeOptions,
82 | ) (
83 | clientLinkURI string,
84 | ) {
85 | if options != nil {
86 | v, _ := query.Values(options)
87 | clientLinkURI = fmt.Sprintf("%s?%s", clientLink, v.Encode())
88 | }
89 |
90 | return
91 | }
92 |
--------------------------------------------------------------------------------
/mollie/client_links_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | "testing"
8 |
9 | "github.com/VictorAvelar/mollie-api-go/v4/testdata"
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | func TestClientLinkService_Create(t *testing.T) {
14 | setEnv()
15 | defer unsetEnv()
16 |
17 | type args struct {
18 | ctx context.Context
19 | cd CreateClientLink
20 | }
21 |
22 | cases := []struct {
23 | name string
24 | args args
25 | wantErr bool
26 | err error
27 | handler http.HandlerFunc
28 | pre func()
29 | }{
30 | {
31 | "create new client link",
32 | args{
33 | context.Background(),
34 | CreateClientLink{},
35 | },
36 | false,
37 | nil,
38 | func(w http.ResponseWriter, r *http.Request) {
39 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
40 | testMethod(t, r, "POST")
41 | if _, ok := r.Header[AuthHeader]; !ok {
42 | w.WriteHeader(http.StatusUnauthorized)
43 | }
44 |
45 | _, _ = w.Write([]byte(testdata.CreateClientLinkResponse))
46 | },
47 | noPre,
48 | },
49 | {
50 | "create client link, an error is returned from the server",
51 | args{
52 | context.Background(),
53 | CreateClientLink{},
54 | },
55 | true,
56 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
57 | errorHandler,
58 | noPre,
59 | },
60 | {
61 | "create client link, an error occurs when parsing json",
62 | args{
63 | context.Background(),
64 | CreateClientLink{},
65 | },
66 | true,
67 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
68 | encodingHandler,
69 | noPre,
70 | },
71 | {
72 | "create client link, invalid url when building request",
73 | args{
74 | context.Background(),
75 | CreateClientLink{},
76 | },
77 | true,
78 | errBadBaseURL,
79 | errorHandler,
80 | crashSrv,
81 | },
82 | }
83 |
84 | for _, c := range cases {
85 | setup()
86 | defer teardown()
87 | t.Run(c.name, func(t *testing.T) {
88 | tMux.HandleFunc(
89 | "/v2/client-links",
90 | c.handler,
91 | )
92 | c.pre()
93 |
94 | res, cb, err := tClient.ClientLinks.Create(c.args.ctx, c.args.cd)
95 | if c.wantErr {
96 | assert.Error(t, err)
97 | assert.EqualError(t, err, c.err.Error())
98 | } else {
99 | assert.Nil(t, err)
100 | assert.EqualValues(t, c.args.ctx, res.Request.Context())
101 | assert.IsType(t, &ClientLink{}, cb)
102 | assert.IsType(t, &http.Response{}, res.Response)
103 | }
104 | })
105 | }
106 | }
107 |
108 | func TestClientLinkService_GetFinalClientLink(t *testing.T) {
109 | setEnv()
110 | defer unsetEnv()
111 |
112 | type args struct {
113 | ctx context.Context
114 | clientLink string
115 | options *ClientLinkAuthorizeOptions
116 | }
117 | tests := []struct {
118 | name string
119 | args args
120 | wantClientLinkURI string
121 | }{
122 | {
123 | "constructs client link finalize step correctly.",
124 | args{
125 | context.Background(),
126 | "https://my.mollie.com/dashboard/client-link/finalize/csr_vZCnNQsV2UtfXxYifWKWH",
127 | &ClientLinkAuthorizeOptions{
128 | ClientID: "app_j9Pakf56Ajta6Y65AkdTtAv",
129 | State: "unique_string_to_compare",
130 | Scope: []PermissionGrant{OnboardingRead, OnboardingWrite},
131 | },
132 | },
133 | "https://my.mollie.com/dashboard/client-link/finalize/csr_vZCnNQsV2UtfXxYifWKWH?clientId=app_j9Pakf56Ajta6Y65AkdTtAv&scope=onboarding.read%2Bonbording.write&state=unique_string_to_compare",
134 | },
135 | {
136 | "constructs client link finalize with complex values",
137 | args{
138 | context.Background(),
139 | "https://my.mollie.com/dashboard/client-link/finalize/csr_vZCnNQsV2UtfXxYifWKWH",
140 | &ClientLinkAuthorizeOptions{
141 | ClientID: "",
142 | State: "\ns\\s\\s\\s\n",
143 | Scope: []PermissionGrant{},
144 | },
145 | },
146 | "https://my.mollie.com/dashboard/client-link/finalize/csr_vZCnNQsV2UtfXxYifWKWH?state=%0As%5Cs%5Cs%5Cs%0A",
147 | },
148 | {
149 | "constructs client link finalize with no query params",
150 | args{
151 | context.Background(),
152 | "https://my.mollie.com/dashboard/client-link/finalize/csr_vZCnNQsV2UtfXxYifWKWH",
153 | &ClientLinkAuthorizeOptions{},
154 | },
155 | "https://my.mollie.com/dashboard/client-link/finalize/csr_vZCnNQsV2UtfXxYifWKWH?",
156 | },
157 | }
158 |
159 | setup()
160 | defer teardown()
161 | for _, tt := range tests {
162 | t.Run(tt.name, func(t *testing.T) {
163 | gotClientLinkURI := tClient.ClientLinks.GetFinalClientLink(tt.args.ctx, tt.args.clientLink, tt.args.options)
164 |
165 | assert.Equal(t, tt.wantClientLinkURI, gotClientLinkURI)
166 | })
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/mollie/clients.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "time"
8 | )
9 |
10 | // LinkedClient describes a single client, linked to your partner account.
11 | type LinkedClient struct {
12 | Resource string `json:"resource,omitempty"`
13 | ID string `json:"id,omitempty"`
14 | OrganizationCreatedAt *time.Time `json:"organizationCreatedAt,omitempty"`
15 | Links LinkedClientLinks `json:"_links,omitempty"`
16 | }
17 |
18 | // LinkedClientLinks contains URL objects relevant to the client.
19 | type LinkedClientLinks struct {
20 | Self *URL `json:"self,omitempty"`
21 | Organization *URL `json:"organization,omitempty"`
22 | Onboarding *URL `json:"onboarding,omitempty"`
23 | Documentation *URL `json:"documentation,omitempty"`
24 | }
25 |
26 | // GetLinkedClientOptions contains valid query parameters for the get clients endpoint.
27 | type GetLinkedClientOptions struct {
28 | Embed []EmbedValue `url:"embed,omitempty"`
29 | }
30 |
31 | // LinkedClientList describes a list of partner clients.
32 | type LinkedClientList struct {
33 | Count int `json:"count,omitempty"`
34 | PartnerClients struct {
35 | Clients []*LinkedClient `json:"clients,omitempty"`
36 | } `json:"_embedded,omitempty"`
37 | Links PaginationLinks `json:"_links,omitempty"`
38 | }
39 |
40 | // ListLinkedClientsOptions contains valid query parameters for the list clients endpoint.
41 | type ListLinkedClientsOptions struct {
42 | Limit int `url:"limit,omitempty"`
43 | From string `url:"from,omitempty"`
44 | Embed []EmbedValue `url:"embed,omitempty"`
45 | }
46 |
47 | // ClientsService operates over the partners API.
48 | type ClientsService service
49 |
50 | // List retrieves all clients.
51 | //
52 | // See: https://docs.mollie.com/reference/list-clients
53 | func (ps *ClientsService) List(ctx context.Context, opts *ListLinkedClientsOptions) (
54 | res *Response,
55 | pc *LinkedClientList,
56 | err error,
57 | ) {
58 | res, err = ps.client.get(ctx, "v2/clients", opts)
59 | if err != nil {
60 | return
61 | }
62 |
63 | if err = json.Unmarshal(res.content, &pc); err != nil {
64 | return
65 | }
66 |
67 | return
68 | }
69 |
70 | // Get retrieves a single client, linked to your partner account, by its ID.
71 | //
72 | // See: https://docs.mollie.com/reference/get-client
73 | func (ps *ClientsService) Get(ctx context.Context, id string, opts *GetLinkedClientOptions) (
74 | res *Response,
75 | pc *LinkedClient,
76 | err error,
77 | ) {
78 | res, err = ps.client.get(ctx, fmt.Sprintf("v2/clients/%s", id), opts)
79 | if err != nil {
80 | return
81 | }
82 |
83 | if err = json.Unmarshal(res.content, &pc); err != nil {
84 | return
85 | }
86 |
87 | return
88 | }
89 |
--------------------------------------------------------------------------------
/mollie/clients_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | "testing"
8 |
9 | "github.com/VictorAvelar/mollie-api-go/v4/testdata"
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | func TestClientsService_Get(t *testing.T) {
14 | setEnv()
15 | defer unsetEnv()
16 |
17 | type args struct {
18 | ctx context.Context
19 | client string
20 | opts *GetLinkedClientOptions
21 | }
22 |
23 | cases := []struct {
24 | name string
25 | args args
26 | wantErr bool
27 | err error
28 | pre func()
29 | handler http.HandlerFunc
30 | }{
31 | {
32 | "get partner client works as expected.",
33 | args{
34 | context.Background(),
35 | "org_1337",
36 | nil,
37 | },
38 | false,
39 | nil,
40 | noPre,
41 | func(w http.ResponseWriter, r *http.Request) {
42 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
43 | testMethod(t, r, "GET")
44 |
45 | if _, ok := r.Header[AuthHeader]; !ok {
46 | w.WriteHeader(http.StatusUnauthorized)
47 | }
48 | _, _ = w.Write([]byte(testdata.GetPartnerClientResponse))
49 | },
50 | },
51 | {
52 | "get partner client with options works as expected.",
53 | args{
54 | context.Background(),
55 | "org_1337",
56 | &GetLinkedClientOptions{
57 | Embed: []EmbedValue{EmbedOrganization},
58 | },
59 | },
60 | false,
61 | nil,
62 | noPre,
63 | func(w http.ResponseWriter, r *http.Request) {
64 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
65 | testMethod(t, r, "GET")
66 | testQuery(t, r, "embed=organization")
67 |
68 | if _, ok := r.Header[AuthHeader]; !ok {
69 | w.WriteHeader(http.StatusUnauthorized)
70 | }
71 | _, _ = w.Write([]byte(testdata.GetPartnerClientResponse))
72 | },
73 | },
74 | {
75 | "get partner client, an error is returned from the server",
76 | args{
77 | context.Background(),
78 | "org_1337",
79 | nil,
80 | },
81 | true,
82 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
83 | noPre,
84 | errorHandler,
85 | },
86 | {
87 | "get partner client, an error occurs when parsing json",
88 | args{
89 | context.Background(),
90 | "org_1337",
91 | nil,
92 | },
93 | true,
94 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
95 | noPre,
96 | encodingHandler,
97 | },
98 | {
99 | "get partner client, invalid url when building request",
100 | args{
101 | context.Background(),
102 | "org_1337",
103 | nil,
104 | },
105 | true,
106 | errBadBaseURL,
107 | crashSrv,
108 | errorHandler,
109 | },
110 | }
111 |
112 | for _, c := range cases {
113 | setup()
114 | defer teardown()
115 |
116 | t.Run(c.name, func(t *testing.T) {
117 | c.pre()
118 | tMux.HandleFunc(fmt.Sprintf("/v2/clients/%s", c.args.client), c.handler)
119 |
120 | res, m, err := tClient.Clients.Get(c.args.ctx, c.args.client, c.args.opts)
121 | if c.wantErr {
122 | assert.NotNil(t, err)
123 | assert.EqualError(t, err, c.err.Error())
124 | } else {
125 | assert.Nil(t, err)
126 | assert.IsType(t, &LinkedClient{}, m)
127 | assert.IsType(t, &http.Response{}, res.Response)
128 | }
129 | })
130 | }
131 | }
132 |
133 | func TestClientService_List(t *testing.T) {
134 | setEnv()
135 | defer unsetEnv()
136 |
137 | type args struct {
138 | ctx context.Context
139 | client string
140 | opts *ListLinkedClientsOptions
141 | }
142 |
143 | cases := []struct {
144 | name string
145 | args args
146 | wantErr bool
147 | err error
148 | pre func()
149 | handler http.HandlerFunc
150 | }{
151 | {
152 | "list partner client works as expected.",
153 | args{
154 | context.Background(),
155 | "org_1337",
156 | nil,
157 | },
158 | false,
159 | nil,
160 | noPre,
161 | func(w http.ResponseWriter, r *http.Request) {
162 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
163 | testMethod(t, r, "GET")
164 |
165 | if _, ok := r.Header[AuthHeader]; !ok {
166 | w.WriteHeader(http.StatusUnauthorized)
167 | }
168 | _, _ = w.Write([]byte(testdata.GetPartnerClientResponse))
169 | },
170 | },
171 | {
172 | "list partner client with options works as expected.",
173 | args{
174 | context.Background(),
175 | "org_1337",
176 | &ListLinkedClientsOptions{
177 | Embed: []EmbedValue{EmbedOrganization},
178 | },
179 | },
180 | false,
181 | nil,
182 | noPre,
183 | func(w http.ResponseWriter, r *http.Request) {
184 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
185 | testMethod(t, r, "GET")
186 | testQuery(t, r, "embed=organization")
187 |
188 | if _, ok := r.Header[AuthHeader]; !ok {
189 | w.WriteHeader(http.StatusUnauthorized)
190 | }
191 | _, _ = w.Write([]byte(testdata.GetPartnerClientResponse))
192 | },
193 | },
194 | {
195 | "list partner client, an error is returned from the server",
196 | args{
197 | context.Background(),
198 | "org_1337",
199 | nil,
200 | },
201 | true,
202 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
203 | noPre,
204 | errorHandler,
205 | },
206 | {
207 | "list partner client, an error occurs when parsing json",
208 | args{
209 | context.Background(),
210 | "org_1337",
211 | nil,
212 | },
213 | true,
214 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
215 | noPre,
216 | encodingHandler,
217 | },
218 | {
219 | "list partner client, invalid url when building request",
220 | args{
221 | context.Background(),
222 | "org_1337",
223 | nil,
224 | },
225 | true,
226 | errBadBaseURL,
227 | crashSrv,
228 | errorHandler,
229 | },
230 | }
231 |
232 | for _, c := range cases {
233 | setup()
234 | defer teardown()
235 |
236 | t.Run(c.name, func(t *testing.T) {
237 | c.pre()
238 | tMux.HandleFunc("/v2/clients", c.handler)
239 |
240 | res, m, err := tClient.Clients.List(c.args.ctx, c.args.opts)
241 | if c.wantErr {
242 | assert.NotNil(t, err)
243 | assert.EqualError(t, err, c.err.Error())
244 | } else {
245 | assert.Nil(t, err)
246 | assert.IsType(t, &LinkedClientList{}, m)
247 | assert.IsType(t, &http.Response{}, res.Response)
248 | }
249 | })
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/mollie/common_types_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "testing"
5 | "time"
6 |
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestShortDate_UnmarshalJSON(t *testing.T) {
11 | type args struct {
12 | b []byte
13 | }
14 | tests := []struct {
15 | name string
16 | args args
17 | wantErr bool
18 | }{
19 | {
20 | "unmarshal fails with invalid date format",
21 | args{b: []byte("30-12-1991")},
22 | true,
23 | },
24 | {
25 | "unmarshal is successful",
26 | args{b: []byte("1991-12-30")},
27 | false,
28 | },
29 | }
30 | for _, tt := range tests {
31 | t.Run(tt.name, func(t *testing.T) {
32 | d := &ShortDate{}
33 | err := d.UnmarshalJSON(tt.args.b)
34 | if tt.wantErr {
35 | assert.NotNil(t, err)
36 | } else {
37 | assert.Nil(t, err)
38 | }
39 | })
40 | }
41 | }
42 |
43 | func TestShortDate_MarshalJSON(t *testing.T) {
44 | t.Run("marshal is successful", func(t *testing.T) {
45 | n := time.Now()
46 | d := &ShortDate{}
47 | d.Time = n
48 | _, err := d.MarshalJSON()
49 | assert.Nil(t, err)
50 | })
51 | }
52 |
--------------------------------------------------------------------------------
/mollie/config.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | // Config contains information that helps during the setup of a new Mollie client.
4 | type Config struct {
5 | testing bool
6 | auth string
7 | reqIdempotency bool
8 | }
9 |
10 | // ToggleTesting enables/disables the test-mode in the current Config.
11 | func (c *Config) ToggleTesting() bool {
12 | c.testing = !c.testing
13 |
14 | return c.testing
15 | }
16 |
17 | // ToggleIdempotency enables/disables the request idempotency feature
18 | // in the current Config.
19 | func (c *Config) ToggleIdempotency() bool {
20 | c.reqIdempotency = !c.reqIdempotency
21 |
22 | return c.reqIdempotency
23 | }
24 |
25 | // SwitchAuthStrategy changes the environment variable used to fetch the
26 | // auth tokens.
27 | //
28 | // Known values are: [MOLLIE_API_TOKEN,MOLLIE_ORG_TOKEN], if you use a custom
29 | // environment variable pass it as argument.
30 | func (c *Config) SwitchAuthStrategy(auth string) string {
31 | c.auth = auth
32 |
33 | return c.auth
34 | }
35 |
36 | /* Configuration init helpers. */
37 |
38 | // NewConfig builds a Mollie configuration object,
39 | // it takes t to indicate if our client is meant to create requests for testing,
40 | // and auth to indicate the authentication method we want to use.
41 | func NewConfig(t bool, auth string) *Config {
42 | return createConfig(t, false, auth)
43 | }
44 |
45 | // NewAPITestingConfig builds a configuration object with the following settings:
46 | // tests mode: enabled
47 | // api token source: MOLLIE_API_TOKEN
48 | //
49 | // it receives `reqIdem (boolean)` to enable the request idempotency feature.
50 | func NewAPITestingConfig(reqIdem bool) *Config {
51 | return createConfig(true, reqIdem, APITokenEnv)
52 | }
53 |
54 | // NewAPIConfig builds a configuration object with the following settings:
55 | // tests mode: disabled
56 | // api token source: MOLLIE_API_TOKEN
57 | //
58 | // it receives `reqIdem (boolean)` to enable the request idempotency feature.
59 | func NewAPIConfig(reqIdem bool) *Config {
60 | return createConfig(false, reqIdem, APITokenEnv)
61 | }
62 |
63 | // NewOrgTestingConfig builds a configuration object with the following settings:
64 | // tests mode: enabled
65 | // api token source: MOLLIE_ORG_TOKEN
66 | //
67 | // it receives `reqIdem (boolean)` to enable the request idempotency feature.
68 | func NewOrgTestingConfig(reqIdem bool) *Config {
69 | return createConfig(true, reqIdem, OrgTokenEnv)
70 | }
71 |
72 | // NewOrgConfig builds a configuration object with the following settings:
73 | // tests mode: disabled
74 | // Org token source: MOLLIE_ORG_TOKEN
75 | //
76 | // it receives `reqIdem (boolean)` to enable the request idempotency feature.
77 | func NewOrgConfig(reqIdem bool) *Config {
78 | return createConfig(false, reqIdem, OrgTokenEnv)
79 | }
80 |
81 | func createConfig(test, reqIdem bool, auth string) *Config {
82 | return &Config{
83 | testing: test,
84 | auth: auth,
85 | reqIdempotency: reqIdem,
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/mollie/config_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestConfig_ToggleTesting(t *testing.T) {
11 | c := NewAPITestingConfig(false)
12 |
13 | assert.True(t, c.testing)
14 | c.ToggleTesting()
15 | assert.False(t, c.testing)
16 | }
17 |
18 | func TestConfig_ToggleIdempotency(t *testing.T) {
19 | c := NewAPITestingConfig(false)
20 |
21 | assert.False(t, c.reqIdempotency)
22 | assert.True(t, c.ToggleIdempotency())
23 | assert.True(t, c.reqIdempotency)
24 | }
25 |
26 | func TestConfig_SwitchAuthStrategy(t *testing.T) {
27 | c := NewAPITestingConfig(false)
28 |
29 | assert.Equal(t, APITokenEnv, c.auth)
30 | c.SwitchAuthStrategy(OrgTokenEnv)
31 | assert.Equal(t, OrgTokenEnv, c.auth)
32 | }
33 |
34 | func TestNewConfig(t *testing.T) {
35 | type args struct {
36 | t bool
37 | auth string
38 | }
39 | tests := []struct {
40 | name string
41 | args args
42 | want *Config
43 | }{
44 | {
45 | "config set to testing and with API token",
46 | args{
47 | t: true,
48 | auth: APITokenEnv,
49 | },
50 | &Config{
51 | testing: true,
52 | auth: "MOLLIE_API_TOKEN",
53 | },
54 | },
55 | {
56 | "config set to testing and with ORG token",
57 | args{
58 | t: true,
59 | auth: OrgTokenEnv,
60 | },
61 | &Config{
62 | testing: true,
63 | auth: "MOLLIE_ORG_TOKEN",
64 | },
65 | },
66 | {
67 | "config set to production and with ORG token",
68 | args{
69 | t: true,
70 | auth: OrgTokenEnv,
71 | },
72 | &Config{
73 | testing: true,
74 | auth: "MOLLIE_ORG_TOKEN",
75 | },
76 | },
77 | {
78 | "config set to production and with API token",
79 | args{
80 | t: true,
81 | auth: APITokenEnv,
82 | },
83 | &Config{
84 | testing: true,
85 | auth: "MOLLIE_API_TOKEN",
86 | },
87 | },
88 | }
89 | for _, tt := range tests {
90 | t.Run(tt.name, func(t *testing.T) {
91 | got := NewConfig(tt.args.t, tt.args.auth)
92 | assert.Equal(t, got, tt.want)
93 | })
94 | }
95 | }
96 |
97 | // ExampleNewConfig demonstrates how to initialize a Config
98 | // struct with the specified values for testing and token source.
99 | func ExampleNewConfig() {
100 | conf := NewConfig(true, APITokenEnv)
101 | fmt.Printf(
102 | "testing config, testing: %v, req_idempotency: %v, token source: %s.",
103 | conf.testing,
104 | conf.reqIdempotency,
105 | conf.auth,
106 | )
107 | // Output: testing config, testing: true, req_idempotency: false, token source: MOLLIE_API_TOKEN.
108 | }
109 |
110 | // ExampleNewAPITestingConfig demonstrates how to initialize a Config
111 | // struct with testing mode enabled, token source from the default API
112 | // token env variable (MOLLIE_API_TOKEN) and request idempotency feature
113 | // enabled.
114 | func ExampleNewAPITestingConfig() {
115 | conf := NewAPITestingConfig(true)
116 | fmt.Printf(
117 | "testing api config, testing: %v, req_idempotency: %v, token source: %s.",
118 | conf.testing,
119 | conf.reqIdempotency,
120 | conf.auth,
121 | )
122 | // Output: testing api config, testing: true, req_idempotency: true, token source: MOLLIE_API_TOKEN.
123 | }
124 |
125 | // ExampleNewOrgTestingConfig demonstrates how to initialize a Config
126 | // struct with testing mode enabled, token source from the default Org
127 | // token env variable (MOLLIE_ORG_TOKEN) and request idempotency feature
128 | // enabled.
129 | func ExampleNewOrgTestingConfig() {
130 | conf := NewOrgTestingConfig(true)
131 | fmt.Printf(
132 | "testing org config, testing: %v, req_idempotency: %v, token source: %s.",
133 | conf.testing,
134 | conf.reqIdempotency,
135 | conf.auth,
136 | )
137 | // Output: testing org config, testing: true, req_idempotency: true, token source: MOLLIE_ORG_TOKEN.
138 | }
139 |
140 | // ExampleNewAPIConfig demonstrates how to initialize a Config
141 | // struct with testing mode disabled, token source from the default API
142 | // token env variable (MOLLIE_API_TOKEN) and request idempotency feature
143 | // enabled.
144 | func ExampleNewAPIConfig() {
145 | conf := NewAPIConfig(true)
146 | fmt.Printf(
147 | "testing api config, testing: %v, req_idempotency: %v, token source: %s.",
148 | conf.testing,
149 | conf.reqIdempotency,
150 | conf.auth,
151 | )
152 | // Output: testing api config, testing: false, req_idempotency: true, token source: MOLLIE_API_TOKEN.
153 | }
154 |
155 | // ExampleNewOrgConfig demonstrates how to initialize a Config struct
156 | // with testing mode disabled, token source from the default Org token
157 | // env variable (MOLLIE_ORG_TOKEN) and request idempotency feature enabled.
158 | func ExampleNewOrgConfig() {
159 | conf := NewOrgConfig(true)
160 | fmt.Printf(
161 | "testing org config, testing: %v, req_idempotency: %v, token source: %s.",
162 | conf.testing,
163 | conf.reqIdempotency,
164 | conf.auth,
165 | )
166 | // Output: testing org config, testing: false, req_idempotency: true, token source: MOLLIE_ORG_TOKEN.
167 | }
168 |
--------------------------------------------------------------------------------
/mollie/custom_types.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "encoding/json"
5 | )
6 |
7 | // ContextValues is a map of TransactionType to ContextValue.
8 | type ContextValues map[TransactionType]ContextValue
9 |
10 | // UnmarshalJSON implements the json.Unmarshaler interface on ContextValues.
11 | //
12 | // See: https://github.com/VictorAvelar/mollie-api-go/issues/251
13 | func (cv *ContextValues) UnmarshalJSON(data []byte) error {
14 | var d map[TransactionType]ContextValue
15 |
16 | if err := json.Unmarshal(data, &d); err != nil {
17 | if _, ok := err.(*json.UnmarshalTypeError); ok {
18 | *cv = make(ContextValues)
19 |
20 | return nil
21 | }
22 |
23 | return err
24 | }
25 |
26 | *cv = ContextValues(d)
27 |
28 | return nil
29 | }
30 |
--------------------------------------------------------------------------------
/mollie/custom_types_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/google/go-querystring/query"
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestContextValues_UnmarshalJSON(t *testing.T) {
11 | tests := []struct {
12 | name string
13 | data []byte
14 | want ContextValues
15 | wantErr bool
16 | }{
17 | // Add test cases here
18 | {
19 | name: "Correct decoding JSON",
20 | data: []byte(`{"type1": "value1", "type2": "value2"}`),
21 | want: ContextValues{
22 | "type1": "value1",
23 | "type2": "value2",
24 | },
25 | wantErr: false,
26 | },
27 | {
28 | name: "Handle empty JSON",
29 | data: []byte(`{}`),
30 | want: ContextValues{},
31 | wantErr: false,
32 | },
33 | {
34 | name: "Invalid JSON",
35 | data: []byte(`{"type1": "value1", "type2": "value2"`),
36 | want: make(ContextValues),
37 | wantErr: true,
38 | },
39 | {
40 | name: "Incorrect type in json returns an empty map",
41 | data: []byte(`{"type1":["value1", "value2"]}`),
42 | want: make(ContextValues),
43 | wantErr: false,
44 | },
45 | {
46 | name: "Test correct case described on issue #251",
47 | data: []byte(`{"context": {
48 | "paymentId": "tr_xxxxxxxx"
49 | }}`),
50 | want: make(ContextValues),
51 | wantErr: false,
52 | },
53 | {
54 | name: "Test failing case described on issue #251",
55 | data: []byte(`{"context": []}`),
56 | want: make(ContextValues),
57 | wantErr: false,
58 | },
59 | }
60 |
61 | for _, tt := range tests {
62 | t.Run(tt.name, func(t *testing.T) {
63 | var cv ContextValues
64 | err := cv.UnmarshalJSON(tt.data)
65 |
66 | if tt.wantErr {
67 | assert.Error(t, err)
68 | } else {
69 | assert.NoError(t, err)
70 | assert.Equal(t, tt.want, cv)
71 | }
72 | })
73 | }
74 | }
75 |
76 | func TestAmount_URLEncodingSimple(t *testing.T) {
77 | tests := []struct {
78 | name string
79 | a Amount
80 | want string
81 | }{
82 | {
83 | name: "Test URL encoding simple.",
84 | a: Amount{
85 | Value: "10.00",
86 | Currency: "EUR",
87 | },
88 | want: "currency=EUR&value=10.00",
89 | },
90 | }
91 | for _, tt := range tests {
92 | t.Run(tt.name, func(t *testing.T) {
93 | v, err := query.Values(tt.a)
94 | assert.Nil(t, err)
95 | assert.Equal(t, tt.want, v.Encode())
96 | })
97 | }
98 | }
99 |
100 | func TestAmount_URLEncodingNested(t *testing.T) {
101 | tests := []struct {
102 | name string
103 | a struct {
104 | Amount Amount `url:"amount"`
105 | }
106 | want string
107 | }{
108 | {
109 | name: "Test URL encoding nested amount in struct.",
110 | a: struct {
111 | Amount Amount `url:"amount"`
112 | }{
113 | Amount{
114 | Value: "10.00",
115 | Currency: "EUR",
116 | },
117 | },
118 | want: "amount%5Bcurrency%5D=EUR&amount%5Bvalue%5D=10.00",
119 | },
120 | }
121 | for _, tt := range tests {
122 | t.Run(tt.name, func(t *testing.T) {
123 | v, err := query.Values(tt.a)
124 | assert.Nil(t, err)
125 | assert.Equal(t, tt.want, v.Encode())
126 | })
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/mollie/customers.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "time"
8 | )
9 |
10 | // CreateCustomer contains the parameters to create a customer.
11 | type CreateCustomer struct {
12 | Name string `json:"name,omitempty"`
13 | Email string `json:"email,omitempty"`
14 | Locale Locale `json:"locale,omitempty"`
15 | Metadata any `json:"metadata,omitempty"`
16 | }
17 |
18 | // UpdateCustomer contains the parameters to update a customer.
19 | type UpdateCustomer struct {
20 | Name string `json:"name,omitempty"`
21 | Email string `json:"email,omitempty"`
22 | Locale Locale `json:"locale,omitempty"`
23 | Metadata any `json:"metadata,omitempty"`
24 | }
25 |
26 | // CustomerLinks contains the HAL resources for a customer response.
27 | type CustomerLinks struct {
28 | Self *URL `json:"self,omitempty"`
29 | Dashboard *URL `json:"dashboard,omitempty"`
30 | Mandates *URL `json:"mandates,omitempty"`
31 | Subscriptions *URL `json:"subscriptions,omitempty"`
32 | Payments *URL `json:"payments,omitempty"`
33 | Documentation *URL `json:"documentation,omitempty"`
34 | }
35 |
36 | // Customer represents buyers.
37 | type Customer struct {
38 | Resource string `json:"resource,omitempty"`
39 | ID string `json:"id,omitempty"`
40 | Mode Mode `json:"mode,omitempty"`
41 | Name string `json:"name,omitempty"`
42 | Email string `json:"email,omitempty"`
43 | Locale Locale `json:"locale,omitempty"`
44 | Metadata any `json:"metadata,omitempty"`
45 | CreatedAt *time.Time `json:"createdAt,omitempty"`
46 | Links CustomerLinks `json:"_links,omitempty"`
47 | }
48 |
49 | // ListCustomersOptions contains valid query parameters for the list customers endpoint.
50 | type ListCustomersOptions struct {
51 | From string `url:"from,omitempty"`
52 | Limit int `url:"limit,omitempty"`
53 | ProfileID string `url:"profileId,omitempty"`
54 | SequenceType SequenceType `url:"sequenceType,omitempty"`
55 | RedirectURL string `url:"redirectUrl,omitempty"`
56 | }
57 |
58 | // CustomersList contains a embedded list of customers
59 | // wrapped in a standard Mollie paginated response.
60 | type CustomersList struct {
61 | Count int `json:"count,omitempty"`
62 | Embedded struct {
63 | Customers []*Customer `json:"customers,omitempty"`
64 | } `json:"_embedded,omitempty"`
65 | Links PaginationLinks `json:"links,omitempty"`
66 | }
67 |
68 | // CustomersService operates over the customer resource.
69 | type CustomersService service
70 |
71 | // Get finds a customer by its ID.
72 | //
73 | // See: https://docs.mollie.com/reference/get-customer
74 | func (cs *CustomersService) Get(ctx context.Context, id string) (res *Response, c *Customer, err error) {
75 | u := fmt.Sprintf("v2/customers/%s", id)
76 |
77 | res, err = cs.client.get(ctx, u, nil)
78 | if err != nil {
79 | return
80 | }
81 |
82 | if err = json.Unmarshal(res.content, &c); err != nil {
83 | return
84 | }
85 |
86 | return
87 | }
88 |
89 | // Create creates a simple minimal representation of a customer in the Mollie API
90 | // to use for the Mollie Checkout and Recurring features.
91 | //
92 | // See: https://docs.mollie.com/reference/create-customer
93 | func (cs *CustomersService) Create(ctx context.Context, c CreateCustomer) (res *Response, cc *Customer, err error) {
94 | res, err = cs.client.post(ctx, "v2/customers", c, nil)
95 | if err != nil {
96 | return
97 | }
98 |
99 | if err = json.Unmarshal(res.content, &cc); err != nil {
100 | return
101 | }
102 |
103 | return
104 | }
105 |
106 | // Update an existing customer.
107 | //
108 | // See: https://docs.mollie.com/reference/update-customer
109 | func (cs *CustomersService) Update(ctx context.Context, id string, c UpdateCustomer) (
110 | res *Response,
111 | cc *Customer,
112 | err error,
113 | ) {
114 | u := fmt.Sprintf("v2/customers/%s", id)
115 |
116 | res, err = cs.client.patch(ctx, u, c)
117 | if err != nil {
118 | return
119 | }
120 |
121 | if err = json.Unmarshal(res.content, &cc); err != nil {
122 | return
123 | }
124 |
125 | return
126 | }
127 |
128 | // Delete a customer.
129 | //
130 | // All mandates and subscriptions created for this customer will be canceled as well.
131 | //
132 | // See: https://docs.mollie.com/reference/delete-customer
133 | func (cs *CustomersService) Delete(ctx context.Context, id string) (res *Response, err error) {
134 | u := fmt.Sprintf("v2/customers/%s", id)
135 |
136 | res, err = cs.client.delete(ctx, u)
137 | if err != nil {
138 | return
139 | }
140 |
141 | return
142 | }
143 |
144 | // List retrieves all customers created.
145 | //
146 | // See: https://docs.mollie.com/reference/list-customers
147 | func (cs *CustomersService) List(ctx context.Context, options *ListCustomersOptions) (
148 | res *Response,
149 | cl *CustomersList,
150 | err error,
151 | ) {
152 | res, err = cs.list(ctx, "v2/customers", options)
153 | if err != nil {
154 | return
155 | }
156 |
157 | if err = json.Unmarshal(res.content, &cl); err != nil {
158 | return
159 | }
160 |
161 | return
162 | }
163 |
164 | // GetPayments retrieves all payments linked to the customer.
165 | //
166 | // See: https://docs.mollie.com/reference/list-customer-payments
167 | func (cs *CustomersService) GetPayments(ctx context.Context, id string, options *ListCustomersOptions) (
168 | res *Response,
169 | pl *PaymentList,
170 | err error,
171 | ) {
172 | u := fmt.Sprintf("v2/customers/%s/payments", id)
173 |
174 | res, err = cs.list(ctx, u, options)
175 | if err != nil {
176 | return
177 | }
178 |
179 | if err = json.Unmarshal(res.content, &pl); err != nil {
180 | return
181 | }
182 |
183 | return
184 | }
185 |
186 | // CreatePayment creates a payment for the customer.
187 | //
188 | // See: https://docs.mollie.com/reference/create-customer-payment
189 | func (cs *CustomersService) CreatePayment(ctx context.Context, id string, p CreatePayment) (
190 | res *Response,
191 | pp *Payment,
192 | err error,
193 | ) {
194 | u := fmt.Sprintf("v2/customers/%s/payments", id)
195 |
196 | res, err = cs.client.post(ctx, u, p, nil)
197 | if err != nil {
198 | return
199 | }
200 |
201 | if err = json.Unmarshal(res.content, &pp); err != nil {
202 | return
203 | }
204 |
205 | return
206 | }
207 |
208 | func (cs *CustomersService) list(ctx context.Context, uri string, options interface{}) (r *Response, err error) {
209 | r, err = cs.client.get(ctx, uri, options)
210 | if err != nil {
211 | return
212 | }
213 |
214 | return
215 | }
216 |
--------------------------------------------------------------------------------
/mollie/doc.go:
--------------------------------------------------------------------------------
1 | // Package mollie is a wrapper around Mollie's REST API.
2 | //
3 | // See: https://www.mollie.com/developers
4 | //
5 | // The Mollie API is a straightforward REST API. This means all endpoints
6 | // either create, retrieve, or update objects. API calls are authenticated
7 | // with an [API credential](https://docs.mollie.com/reference/authentication).
8 | // In some cases, you can receive live updates via [webhooks](https://docs.mollie.com/reference/webhooks).
9 | package mollie
10 |
--------------------------------------------------------------------------------
/mollie/errors.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import "fmt"
4 |
5 | // ErrorLinks container references to common urls
6 | // returned with errors.
7 | type ErrorLinks struct {
8 | Documentation *URL `json:"documentation,omitempty"`
9 | }
10 |
11 | // BaseError contains the general error structure
12 | // returned by mollie.
13 | type BaseError struct {
14 | Status int `json:"status,omitempty"`
15 | Title string `json:"title,omitempty"`
16 | Detail string `json:"detail,omitempty"`
17 | Field string `json:"field,omitempty"`
18 | Links *ErrorLinks `json:"_links,omitempty"`
19 | }
20 |
21 | // Error interface compliance.
22 | func (be *BaseError) Error() string {
23 | str := fmt.Sprintf("%d %s: %s", be.Status, be.Title, be.Detail)
24 |
25 | if len(be.Field) > 0 {
26 | str = fmt.Sprintf("%s, affected field: %s", str, be.Field)
27 | }
28 |
29 | return str
30 | }
31 |
--------------------------------------------------------------------------------
/mollie/errors_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "encoding/json"
5 | "testing"
6 |
7 | "github.com/VictorAvelar/mollie-api-go/v4/testdata"
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestBaseError(t *testing.T) {
12 | cases := []struct {
13 | name string
14 | body []byte
15 | wantField bool
16 | }{
17 | {
18 | "standard base error",
19 | []byte(testdata.UnauthorizedErrorResponse),
20 | false,
21 | },
22 | {
23 | "error with field included",
24 | []byte(testdata.UnprocessableEntityErrorResponse),
25 | true,
26 | },
27 | }
28 |
29 | for _, c := range cases {
30 | g := &BaseError{}
31 | t.Run(c.name, func(t *testing.T) {
32 | err := json.Unmarshal(c.body, g)
33 | assert.Nil(t, err)
34 | if c.wantField {
35 | assert.Contains(t, g.Error(), "field")
36 | } else {
37 | assert.NotContains(t, g.Error(), "field")
38 | }
39 | })
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/mollie/gift_cards.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | // GiftCardIssuer type describes issuers supported
4 | // by mollie.
5 | type GiftCardIssuer string
6 |
7 | // Supported gift card issuers.
8 | // #nosec G101 -- This are the brands issuing gift cards.
9 | const (
10 | BloemenCadeuKaart GiftCardIssuer = "bloemencadeaukaart"
11 | BloemPlantGiftCard GiftCardIssuer = "bloemplantgiftcard"
12 | Boekenbon GiftCardIssuer = "boekenbon"
13 | DaGiftCard GiftCardIssuer = "dagiftcard"
14 | DecaudeuKaart GiftCardIssuer = "decadeaukaart"
15 | DelokaleDecauKaart GiftCardIssuer = "delokalecadeaukaart"
16 | Dinercadeau GiftCardIssuer = "dinercadeau"
17 | Doenkadotickets GiftCardIssuer = "doenkadotickets"
18 | Fashioncheque GiftCardIssuer = "fashioncheque"
19 | Festivalcadeau GiftCardIssuer = "festivalcadeau"
20 | Good4fun GiftCardIssuer = "good4fun"
21 | Horseandgifts GiftCardIssuer = "horseandgifts"
22 | HuistuinCadeauKaart GiftCardIssuer = "huistuincadeaukaart"
23 | JewelCard GiftCardIssuer = "jewelcard"
24 | KlusCadeu GiftCardIssuer = "kluscadeau"
25 | Kunstencultuurcadeaukaart GiftCardIssuer = "kunstencultuurcadeaukaart"
26 | Nationalebioscoopbon GiftCardIssuer = "nationalebioscoopbon"
27 | Nationaleentertainmentcard GiftCardIssuer = "nationaleentertainmentcard"
28 | Nationalegolfbon GiftCardIssuer = "nationalegolfbon"
29 | Ohmygood GiftCardIssuer = "ohmygood"
30 | Podiumcadeaukaart GiftCardIssuer = "podiumcadeaukaart"
31 | Reiscadeau GiftCardIssuer = "reiscadeau"
32 | Restaurantcadeau GiftCardIssuer = "restaurantcadeau"
33 | Shoesandsneakerscadeu GiftCardIssuer = "shoesandsneakerscadeau"
34 | SodexoSportCulturePass GiftCardIssuer = "sodexosportculturepass"
35 | Sportenfitcadeau GiftCardIssuer = "sportenfitcadeau"
36 | Sustainablefashion GiftCardIssuer = "sustainablefashion"
37 | Travelcheq GiftCardIssuer = "travelcheq"
38 | Vvvgiftcard GiftCardIssuer = "vvvgiftcard"
39 | Vvvdinercheque GiftCardIssuer = "vvvdinercheque"
40 | Vvvlekkerweg GiftCardIssuer = "vvvlekkerweg"
41 | Webshopgiftcard GiftCardIssuer = "webshopgiftcard"
42 | Wijncadeukaart GiftCardIssuer = "wijncadeaukaart"
43 | Yourgift GiftCardIssuer = "yourgift"
44 | )
45 |
46 | // IssuerStatus describes the status of a gift
47 | // card issuer in your account.
48 | type IssuerStatus string
49 |
50 | // Valid issuer statuses.
51 | const (
52 | PendingIssuer IssuerStatus = "pending-issuer"
53 | EnabledIssuer IssuerStatus = "enabled"
54 | )
55 |
56 | // GiftCardEnabled describes the response of a gift card
57 | // issuer enable operation.
58 | type GiftCardEnabled struct {
59 | Resource string `json:"resource,omitempty"`
60 | ID GiftCardIssuer `json:"id,omitempty"`
61 | Description string `json:"description,omitempty"`
62 | Status IssuerStatus `json:"status,omitempty"`
63 | Links GiftCardLinks `json:"_links,omitempty"`
64 | }
65 |
66 | // GiftCardLinks are links embedded when a gift card is enabled.
67 | type GiftCardLinks struct {
68 | Self *URL `json:"self,omitempty"`
69 | Documentation *URL `json:"documentation,omitempty"`
70 | }
71 |
--------------------------------------------------------------------------------
/mollie/invoices.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | )
8 |
9 | // InvoiceStatus status of the invoice.
10 | type InvoiceStatus string
11 |
12 | // Valid status of the invoice.
13 | const (
14 | InvoiceStatusOpen InvoiceStatus = "open"
15 | InvoiceStatusPaid InvoiceStatus = "paid"
16 | InvoiceStatusOverdue InvoiceStatus = "overdue"
17 | )
18 |
19 | // Invoice describes an invoice details.
20 | type Invoice struct {
21 | Resource string `json:"resource,omitempty"`
22 | ID string `json:"id,omitempty"`
23 | Reference string `json:"reference,omitempty"`
24 | VatNumber string `json:"vatNumber,omitempty"`
25 | IssuedAt string `json:"issuedAt,omitempty"`
26 | PaidAt string `json:"paidAt,omitempty"`
27 | DueAt string `json:"dueAt,omitempty"`
28 | NetAmount *Amount `json:"netAmount,omitempty"`
29 | VatAmount *Amount `json:"vatAmount,omitempty"`
30 | GrossAmount *Amount `json:"grossAmount,omitempty"`
31 | Lines []*LineItem `json:"lines,omitempty"`
32 | Status InvoiceStatus `json:"status,omitempty"`
33 | Links InvoiceLinks `json:"_links,omitempty"`
34 | }
35 |
36 | // LineItem product details.
37 | type LineItem struct {
38 | Count int64 `json:"count,omitempty"`
39 | VatPercentage float64 `json:"vatPercentage,omitempty"`
40 | Period string `json:"period,omitempty"`
41 | Description string `json:"description,omitempty"`
42 | Amount *Amount `json:"amount,omitempty"`
43 | }
44 |
45 | // InvoiceLinks describes all the possible links to be returned with
46 | // a invoice object.
47 | type InvoiceLinks struct {
48 | Self *URL `json:"self,omitempty"`
49 | PDF *URL `json:"pdf,omitempty"`
50 | Documentation *URL `json:"documentation,omitempty"`
51 | }
52 |
53 | // ListInvoicesOptions describes list invoices endpoint valid query string parameters.
54 | type ListInvoicesOptions struct {
55 | Limit int64 `url:"limit,omitempty"`
56 | Reference string `url:"reference,omitempty"`
57 | Year string `url:"year,omitempty"`
58 | From string `url:"from,omitempty"`
59 | }
60 |
61 | // InvoicesList describes how a list of invoices will be retrieved by Mollie.
62 | type InvoicesList struct {
63 | Count int `json:"count,omitempty"`
64 | Embedded struct {
65 | Invoices []*Invoice `json:"invoices"`
66 | } `json:"_embedded,omitempty"`
67 | Links PaginationLinks `json:"_links,omitempty"`
68 | }
69 |
70 | // InvoicesService instance operates over invoice resources.
71 | type InvoicesService service
72 |
73 | // Get retrieve details of an invoice, using the invoice’s identifier.
74 | func (is *InvoicesService) Get(ctx context.Context, id string) (res *Response, i *Invoice, err error) {
75 | u := fmt.Sprintf("v2/invoices/%s", id)
76 |
77 | res, err = is.client.get(ctx, u, nil)
78 | if err != nil {
79 | return
80 | }
81 |
82 | if err = json.Unmarshal(res.content, &i); err != nil {
83 | return
84 | }
85 |
86 | return
87 | }
88 |
89 | // List retrieves a list of invoices associated with your account/organization.
90 | func (is *InvoicesService) List(ctx context.Context, options *ListInvoicesOptions) (
91 | res *Response,
92 | il *InvoicesList,
93 | err error,
94 | ) {
95 | res, err = is.client.get(ctx, "v2/invoices", options)
96 | if err != nil {
97 | return
98 | }
99 |
100 | if err = json.Unmarshal(res.content, &il); err != nil {
101 | return
102 | }
103 |
104 | return
105 | }
106 |
--------------------------------------------------------------------------------
/mollie/invoices_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | "strconv"
8 | "testing"
9 | "time"
10 |
11 | "github.com/VictorAvelar/mollie-api-go/v4/testdata"
12 | "github.com/stretchr/testify/assert"
13 | )
14 |
15 | func TestInvoicesService_Get(t *testing.T) {
16 | setEnv()
17 | defer unsetEnv()
18 |
19 | type args struct {
20 | ctx context.Context
21 | invoice string
22 | options *ListInvoicesOptions
23 | }
24 |
25 | cases := []struct {
26 | name string
27 | args args
28 | wantErr bool
29 | err error
30 | pre func()
31 | handler http.HandlerFunc
32 | }{
33 | {
34 | "get invoice works as expecter",
35 | args{
36 | context.Background(),
37 | "inv_xBEbP9rvAq",
38 | nil,
39 | },
40 | false,
41 | nil,
42 | noPre,
43 | func(w http.ResponseWriter, r *http.Request) {
44 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
45 | testMethod(t, r, "GET")
46 | if _, ok := r.Header[AuthHeader]; !ok {
47 | w.WriteHeader(http.StatusUnauthorized)
48 | }
49 |
50 | w.WriteHeader(http.StatusOK)
51 | _, _ = w.Write([]byte(testdata.GetInvoiceResponse))
52 | },
53 | },
54 | {
55 | "get invoice, an error is returned from the server",
56 | args{
57 | context.Background(),
58 | "inv_xBEbP9rvAq",
59 | nil,
60 | },
61 | true,
62 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
63 | noPre,
64 | errorHandler,
65 | },
66 | {
67 | "get invoice, an error occurs when parsing json",
68 | args{
69 | context.Background(),
70 | "inv_xBEbP9rvAq",
71 | nil,
72 | },
73 | true,
74 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
75 | noPre,
76 | encodingHandler,
77 | },
78 | {
79 | "get invoice, invalid url when building request",
80 | args{
81 | context.Background(),
82 | "inv_xBEbP9rvAq",
83 | nil,
84 | },
85 | true,
86 | errBadBaseURL,
87 | crashSrv,
88 | errorHandler,
89 | },
90 | }
91 |
92 | for _, c := range cases {
93 | setup()
94 | defer teardown()
95 | t.Run(c.name, func(t *testing.T) {
96 | c.pre()
97 | tMux.HandleFunc(fmt.Sprintf("/v2/invoices/%s", c.args.invoice), c.handler)
98 |
99 | res, i, err := tClient.Invoices.Get(c.args.ctx, c.args.invoice)
100 | if c.wantErr {
101 | assert.NotNil(t, err)
102 | assert.EqualError(t, err, c.err.Error())
103 | } else {
104 | assert.Nil(t, err)
105 | assert.IsType(t, &Invoice{}, i)
106 | assert.EqualValues(t, c.args.ctx, res.Request.Context())
107 | assert.IsType(t, &http.Response{}, res.Response)
108 | }
109 | })
110 | }
111 | }
112 |
113 | func TestInvoicesService_List(t *testing.T) {
114 | setEnv()
115 | defer unsetEnv()
116 |
117 | type args struct {
118 | ctx context.Context
119 | invoice string
120 | options *ListInvoicesOptions
121 | }
122 |
123 | cases := []struct {
124 | name string
125 | args args
126 | wantErr bool
127 | err error
128 | pre func()
129 | handler http.HandlerFunc
130 | }{
131 | {
132 | "list invoices works as expecter",
133 | args{
134 | context.Background(),
135 | "inv_xBEbP9rvAq",
136 | nil,
137 | },
138 | false,
139 | nil,
140 | noPre,
141 | func(w http.ResponseWriter, r *http.Request) {
142 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
143 | testMethod(t, r, "GET")
144 | if _, ok := r.Header[AuthHeader]; !ok {
145 | w.WriteHeader(http.StatusUnauthorized)
146 | }
147 |
148 | w.WriteHeader(http.StatusOK)
149 | _, _ = w.Write([]byte(testdata.ListInvoicesResponse))
150 | },
151 | },
152 | {
153 | "list invoices with options works as expecter",
154 | args{
155 | context.Background(),
156 | "inv_xBEbP9rvAq",
157 | &ListInvoicesOptions{
158 | Year: strconv.Itoa(time.Now().Year()),
159 | },
160 | },
161 | false,
162 | nil,
163 | noPre,
164 | func(w http.ResponseWriter, r *http.Request) {
165 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
166 | testMethod(t, r, "GET")
167 | if _, ok := r.Header[AuthHeader]; !ok {
168 | w.WriteHeader(http.StatusUnauthorized)
169 | }
170 |
171 | w.WriteHeader(http.StatusOK)
172 | _, _ = w.Write([]byte(testdata.ListInvoicesResponse))
173 | },
174 | },
175 | {
176 | "list invoices, an error is returned from the server",
177 | args{
178 | context.Background(),
179 | "inv_xBEbP9rvAq",
180 | nil,
181 | },
182 | true,
183 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
184 | noPre,
185 | errorHandler,
186 | },
187 | {
188 | "list invoices, an error occurs when parsing json",
189 | args{
190 | context.Background(),
191 | "inv_xBEbP9rvAq",
192 | nil,
193 | },
194 | true,
195 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
196 | noPre,
197 | encodingHandler,
198 | },
199 | {
200 | "list invoices, invalid url when building request",
201 | args{
202 | context.Background(),
203 | "inv_xBEbP9rvAq",
204 | nil,
205 | },
206 | true,
207 | errBadBaseURL,
208 | crashSrv,
209 | errorHandler,
210 | },
211 | }
212 |
213 | for _, c := range cases {
214 | setup()
215 | defer teardown()
216 | t.Run(c.name, func(t *testing.T) {
217 | c.pre()
218 | tMux.HandleFunc("/v2/invoices", c.handler)
219 |
220 | res, i, err := tClient.Invoices.List(c.args.ctx, c.args.options)
221 | if c.wantErr {
222 | assert.NotNil(t, err)
223 | assert.EqualError(t, err, c.err.Error())
224 | } else {
225 | assert.Nil(t, err)
226 | assert.IsType(t, &InvoicesList{}, i)
227 | assert.EqualValues(t, c.args.ctx, res.Request.Context())
228 | assert.IsType(t, &http.Response{}, res.Response)
229 | }
230 | })
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/mollie/onboarding.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "time"
7 | )
8 |
9 | const onboardingURLPath = "v2/onboarding/me"
10 |
11 | // OnboardingStatus describes status of the organization’s onboarding process.
12 | type OnboardingStatus string
13 |
14 | // Possible status values.
15 | const (
16 | NeedsDataOnboardingStatus OnboardingStatus = "needs-data"
17 | InReviewOnboardingStatus OnboardingStatus = "in-review"
18 | CompletedOnboardingStatus OnboardingStatus = "completed"
19 | )
20 |
21 | // OnboardingLinks contains URL objects relevant to the onboarding status.
22 | type OnboardingLinks struct {
23 | Self *URL `json:"self,omitempty"`
24 | Dashboard *URL `json:"dashboard,omitempty"`
25 | Organization *URL `json:"organization,omitempty"`
26 | Documentation *URL `json:"documentation,omitempty"`
27 | }
28 |
29 | // Onboarding data for an organization.
30 | type Onboarding struct {
31 | CanReceivePayments bool `json:"canReceivePayments,omitempty"`
32 | CanReceiveSettlements bool `json:"canReceiveSettlements,omitempty"`
33 | Resource string `json:"reference,omitempty"`
34 | Name string `json:"name,omitempty"`
35 | SignedUpAt *time.Time `json:"signedUpAt,omitempty"`
36 | Status OnboardingStatus `json:"status,omitempty"`
37 | Links OnboardingLinks `json:"_links,omitempty"`
38 | }
39 |
40 | // OnboardingData request possible values.
41 | //
42 | // Please note that even though all parameters are optional,
43 | // at least one of them needs to be provided in the request.
44 | //
45 | // Information that the merchant has entered in their dashboard will not be overwritten.
46 |
47 | // OnboardingDataOrganization contains data of the organization you want to provide.
48 | type OnboardingDataOrganization struct {
49 | Name string `json:"name,omitempty"`
50 | RegistrationNumber string `json:"registrationNumber,omitempty"`
51 | VatNumber string `json:"vatNumber,omitempty"`
52 | VatRegulation string `json:"vatRegulation,omitempty"`
53 | Address *Address `json:"address,omitempty"`
54 | }
55 |
56 | // OnboardingDataProfile contains data of the payment profile you want to provide.
57 | type OnboardingDataProfile struct {
58 | Name string `json:"name,omitempty"`
59 | URL string `json:"url,omitempty"`
60 | Email string `json:"email,omitempty"`
61 | Description string `json:"description,omitempty"`
62 | Phone string `json:"phone,omitempty"`
63 | BusinessCategory BusinessCategory `json:"businessCategory,omitempty"`
64 | }
65 |
66 | // OnboardingData describes the information to be submitted.
67 | type OnboardingData struct {
68 | Organization OnboardingDataOrganization `json:"organization,omitempty"`
69 | Profile OnboardingDataProfile `json:"profile,omitempty"`
70 | }
71 |
72 | // OnboardingService operates over the onboarding API.
73 | type OnboardingService service
74 |
75 | // GetOnboardingStatus gets the status of onboarding of the authenticated organization.
76 | //
77 | // See: https://docs.mollie.com/reference/get-onboarding-status
78 | func (os *OnboardingService) GetOnboardingStatus(ctx context.Context) (res *Response, o *Onboarding, err error) {
79 | res, err = os.client.get(ctx, onboardingURLPath, nil)
80 | if err != nil {
81 | return
82 | }
83 |
84 | if err = json.Unmarshal(res.content, &o); err != nil {
85 | return
86 | }
87 |
88 | return
89 | }
90 |
91 | // SubmitOnboardingData sends data that will be prefilled in the merchant’s onboarding.
92 | // Please note that the data you submit will only be processed when the onboarding status is needs-data.
93 | //
94 | // This endpoint has been deprecated. It will be supported for the foreseeable future, but new implementations should
95 | // use the Create client link endpoint to create new clients and submit their organization’s details in one go.
96 | //
97 | // See: https://docs.mollie.com/reference/submit-onboarding-data
98 | func (os *OnboardingService) SubmitOnboardingData(ctx context.Context, d *OnboardingData) (res *Response, err error) {
99 | res, err = os.client.post(ctx, onboardingURLPath, d, nil)
100 | if err != nil {
101 | return
102 | }
103 |
104 | return
105 | }
106 |
--------------------------------------------------------------------------------
/mollie/onboarding_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | "testing"
8 |
9 | "github.com/VictorAvelar/mollie-api-go/v4/testdata"
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | func TestOnboardingService_GetOnboardingStatus(t *testing.T) {
14 | setEnv()
15 | defer unsetEnv()
16 |
17 | cases := []struct {
18 | name string
19 | wantErr bool
20 | err error
21 | pre func()
22 | handler http.HandlerFunc
23 | }{
24 | {
25 | "get onboarding status works as expected.",
26 | false,
27 | nil,
28 | noPre,
29 | func(w http.ResponseWriter, r *http.Request) {
30 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
31 | testMethod(t, r, "GET")
32 |
33 | if _, ok := r.Header[AuthHeader]; !ok {
34 | w.WriteHeader(http.StatusUnauthorized)
35 | }
36 | _, _ = w.Write([]byte(testdata.GetOnboardingStatusResponse))
37 | },
38 | },
39 | {
40 | "get onboarding status, an error is returned from the server",
41 | true,
42 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
43 | noPre,
44 | errorHandler,
45 | },
46 | {
47 | "get onboarding status, an error occurs when parsing json",
48 | true,
49 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
50 | noPre,
51 | encodingHandler,
52 | },
53 | {
54 | "get onboarding status, invalid url when building request",
55 | true,
56 | errBadBaseURL,
57 | crashSrv,
58 | errorHandler,
59 | },
60 | }
61 |
62 | for _, c := range cases {
63 | setup()
64 | defer teardown()
65 |
66 | t.Run(c.name, func(t *testing.T) {
67 | c.pre()
68 | tMux.HandleFunc("/v2/onboarding/me", c.handler)
69 |
70 | res, m, err := tClient.Onboarding.GetOnboardingStatus(context.Background())
71 | if c.wantErr {
72 | assert.NotNil(t, err)
73 | assert.EqualError(t, err, c.err.Error())
74 | } else {
75 | assert.Nil(t, err)
76 | assert.IsType(t, &Onboarding{}, m)
77 | assert.IsType(t, &http.Response{}, res.Response)
78 | }
79 | })
80 | }
81 | }
82 |
83 | func TestOnboardingService_SubmitOnboardingData(t *testing.T) {
84 | setEnv()
85 | defer unsetEnv()
86 |
87 | cases := []struct {
88 | name string
89 | data *OnboardingData
90 | wantErr bool
91 | err error
92 | pre func()
93 | handler http.HandlerFunc
94 | }{
95 | {
96 | "get onboarding status works as expected.",
97 | &OnboardingData{},
98 | false,
99 | nil,
100 | noPre,
101 | func(w http.ResponseWriter, r *http.Request) {
102 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
103 | testMethod(t, r, "POST")
104 |
105 | if _, ok := r.Header[AuthHeader]; !ok {
106 | w.WriteHeader(http.StatusUnauthorized)
107 | }
108 | _, _ = w.Write([]byte(testdata.GetOnboardingStatusResponse))
109 | },
110 | },
111 | {
112 | "get onboarding status, an error is returned from the server",
113 | &OnboardingData{},
114 | true,
115 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
116 | noPre,
117 | errorHandler,
118 | },
119 | {
120 | "get onboarding status, invalid url when building request",
121 | &OnboardingData{},
122 | true,
123 | errBadBaseURL,
124 | crashSrv,
125 | errorHandler,
126 | },
127 | }
128 |
129 | for _, c := range cases {
130 | setup()
131 | defer teardown()
132 |
133 | t.Run(c.name, func(t *testing.T) {
134 | c.pre()
135 | tMux.HandleFunc("/v2/onboarding/me", c.handler)
136 |
137 | res, err := tClient.Onboarding.SubmitOnboardingData(context.Background(), c.data)
138 | if c.wantErr {
139 | assert.NotNil(t, err)
140 | assert.EqualError(t, err, c.err.Error())
141 | } else {
142 | assert.Nil(t, err)
143 | assert.IsType(t, &http.Response{}, res.Response)
144 | }
145 | })
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/mollie/organizations.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "time"
8 | )
9 |
10 | // Organization describes an organization detail.
11 | type Organization struct {
12 | Resource string `json:"resource,omitempty"`
13 | ID string `json:"id,omitempty"`
14 | Name string `json:"name,omitempty"`
15 | Email string `json:"email,omitempty"`
16 | Locale string `json:"locale,omitempty"`
17 | Address *Address `json:"address,omitempty"`
18 | RegistrationNumber string `json:"registrationNumber,omitempty"`
19 | VatNumber string `json:"vatNumber,omitempty"`
20 | VatRegulation string `json:"vatRegulation,omitempty"`
21 | Links OrganizationLinks `json:"_links,omitempty"`
22 | }
23 |
24 | // OrganizationLinks describes all the possible links to be returned with
25 | // a organization object.
26 | type OrganizationLinks struct {
27 | Self *URL `json:"self,omitempty"`
28 | Chargebacks *URL `json:"chargebacks,omitempty"`
29 | Customers *URL `json:"customers,omitempty"`
30 | Dashboard *URL `json:"dashboard,omitempty"`
31 | Invoices *URL `json:"invoices,omitempty"`
32 | Payments *URL `json:"payments,omitempty"`
33 | Profiles *URL `json:"profiles,omitempty"`
34 | Refunds *URL `json:"refunds,omitempty"`
35 | Settlements *URL `json:"settlements,omitempty"`
36 | Documentation *URL `json:"documentation,omitempty"`
37 | }
38 |
39 | // PartnerType alias for organization partner types.
40 | type PartnerType string
41 |
42 | // Available partner types.
43 | const (
44 | PartnerTypeOauth PartnerType = "oauth"
45 | PartnerTypeSignUpLink PartnerType = "signuplink"
46 | PartnerTypeUserAgent PartnerType = "useragent"
47 | )
48 |
49 | // UserAgentToken are time limited valid access tokens.
50 | type UserAgentToken struct {
51 | Token string
52 | StartsAt *time.Time
53 | EndsAt *time.Time
54 | }
55 |
56 | // OrganizationPartnerLinks is an object with several URL objects
57 | // relevant to the partner resource.
58 | type OrganizationPartnerLinks struct {
59 | Self *URL `json:"self,omitempty"`
60 | Documentation *URL `json:"documentation,omitempty"`
61 | SignUpLink *URL `json:"signuplink,omitempty"`
62 | }
63 |
64 | // OrganizationPartnerStatus response descriptor.
65 | type OrganizationPartnerStatus struct {
66 | IsCommissionPartner bool `json:"isCommissionPartner,omitempty"`
67 | PartnerContractUpdateAvailable bool `json:"partnerContractUpdate_available,omitempty"`
68 | Resource string `json:"resource,omitempty"`
69 | PartnerType PartnerType `json:"partnerType,omitempty"`
70 | UserAgentTokens []*UserAgentToken `json:"userAgentTokens,omitempty"`
71 | PartnerContractSignedAt *time.Time `json:"partnerContractSignedAt,omitempty"`
72 | PartnerContractExpiresAt *time.Time `json:"partnerContractExpiresAt,omitempty"`
73 | Links OrganizationPartnerLinks `json:"_links,omitempty"`
74 | }
75 |
76 | // OrganizationsService instance operates over organization resources.
77 | type OrganizationsService service
78 |
79 | // Get retrieve an organization by its id.
80 | func (os *OrganizationsService) Get(ctx context.Context, id string) (res *Response, o *Organization, err error) {
81 | return os.get(ctx, fmt.Sprintf("v2/organizations/%s", id))
82 | }
83 |
84 | // GetCurrent retrieve the currently authenticated organization.
85 | func (os *OrganizationsService) GetCurrent(ctx context.Context) (res *Response, o *Organization, err error) {
86 | return os.get(ctx, "v2/organizations/me")
87 | }
88 |
89 | // GetPartnerStatus retrieves details about the partner status
90 | // of the currently authenticated organization.
91 | //
92 | // See: https://docs.mollie.com/reference/get-partner-status
93 | func (os *OrganizationsService) GetPartnerStatus(ctx context.Context) (
94 | res *Response,
95 | ops *OrganizationPartnerStatus,
96 | err error,
97 | ) {
98 | res, err = os.client.get(ctx, "v2/organizations/me/partner", nil)
99 | if err != nil {
100 | return
101 | }
102 |
103 | if err = json.Unmarshal(res.content, &ops); err != nil {
104 | return
105 | }
106 |
107 | return
108 | }
109 |
110 | func (os *OrganizationsService) get(ctx context.Context, uri string) (res *Response, o *Organization, err error) {
111 | res, err = os.client.get(ctx, uri, nil)
112 | if err != nil {
113 | return
114 | }
115 |
116 | if err = json.Unmarshal(res.content, &o); err != nil {
117 | return
118 | }
119 |
120 | return
121 | }
122 |
--------------------------------------------------------------------------------
/mollie/payment_methods.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | )
8 |
9 | // PaymentMethodStatus tels the status that the method is in.
10 | // Possible values: activated pending-boarding pending-review
11 | // pending-external rejected.
12 | type PaymentMethodStatus string
13 |
14 | // Available payment method statuses.
15 | const (
16 | PaymentMethodActivated PaymentMethodStatus = "activated"
17 | PaymentMethodPendingBoarding PaymentMethodStatus = "pending-boarding"
18 | PaymentMethodPendingReview PaymentMethodStatus = "pending-review"
19 | PaymentMethodPendingExternal PaymentMethodStatus = "pending-external"
20 | PaymentMethodRejected PaymentMethodStatus = "pending-rejected"
21 | )
22 |
23 | // PaymentMethodDetails describes a single method with details.
24 | type PaymentMethodDetails struct {
25 | Resource string `json:"resource,omitempty"`
26 | ID string `json:"id,omitempty"`
27 | Description string `json:"description,omitempty"`
28 | MinimumAmount *Amount `json:"minimumAmount,omitempty"`
29 | MaximumAmount *Amount `json:"maximumAmount,omitempty"`
30 | Image *Image `json:"image,omitempty"`
31 | Pricing []*PaymentMethodPricing `json:"pricing,omitempty"`
32 | Issuers []*PaymentMethodIssuer `json:"issuers,omitempty"`
33 | Status *PaymentMethodStatus `json:"status,omitempty"`
34 | Links MethodsLinks `json:"_links,omitempty"`
35 | }
36 |
37 | // MethodsLinks describes links attached to methods service responses.
38 | type MethodsLinks struct {
39 | Self *URL `json:"self,omitempty"`
40 | Documentation *URL `json:"documentation,omitempty"`
41 | }
42 |
43 | // PaymentMethodPricing contains information about commissions and fees
44 | // applicable to a payment method.
45 | type PaymentMethodPricing struct {
46 | Description string `json:"description,omitempty"`
47 | Variable string `json:"variable,omitempty"`
48 | Fixed *Amount `json:"fixed,omitempty"`
49 | FeeRegion FeeRegion `json:"feeRegion,omitempty"`
50 | }
51 |
52 | // PaymentMethodIssuer available for the payment method
53 | // (for iDEAL, KBC/CBC payment button, gift cards, or meal vouchers).
54 | type PaymentMethodIssuer struct {
55 | Resource string `json:"resource,omitempty"`
56 | ID string `json:"id,omitempty"`
57 | Name string `json:"name,omitempty"`
58 | Image *Image `json:"image,omitempty"`
59 | }
60 |
61 | // PaymentMethodsList describes a list of paginated payment methods.
62 | type PaymentMethodsList struct {
63 | Count int `json:"count,omitempty"`
64 | Embedded struct {
65 | Methods []*PaymentMethodDetails
66 | } `json:"_embedded,omitempty"`
67 | Links PaginationLinks `json:"_links,omitempty"`
68 | }
69 |
70 | // PaymentMethodOptions are applicable query string parameters to get methods
71 | // from mollie's API.
72 | type PaymentMethodOptions struct {
73 | Locale Locale `url:"locale,omitempty"`
74 | Currency string `url:"currency,omitempty"`
75 | ProfileID string `url:"profileId,omitempty"`
76 | Include []IncludeValue `url:"include,omitempty"`
77 | }
78 |
79 | // ListPaymentMethodsOptions are applicable query string parameters to list methods
80 | // from mollie's API.
81 | //
82 | // It contains list specific options and embeds GetMethodOptions.
83 | type ListPaymentMethodsOptions struct {
84 | PaymentMethodOptions
85 | Resource string `url:"resource,omitempty"`
86 | BillingCountry string `url:"billingCountry,omitempty"`
87 | Amount *Amount `url:"amount,omitempty"`
88 | IncludeWallets []Wallet `url:"includeWallets,omitempty"`
89 | OrderLineCategories []OrderLineOperationProductCategory `url:"orderLineCategories,omitempty"`
90 | Locale Locale `url:"locale,omitempty"`
91 | SequenceType SequenceType `url:"sequenceType,omitempty"`
92 | }
93 |
94 | // PaymentMethodsService operates on methods endpoints.
95 | type PaymentMethodsService service
96 |
97 | // Get returns information about the payment method specified by id,
98 | // it also receives a pointer to the method options containing applicable
99 | // query string parameters.
100 | //
101 | // See: https://docs.mollie.com/reference/get-method
102 | func (ms *PaymentMethodsService) Get(ctx context.Context, id PaymentMethod, options *PaymentMethodOptions) (
103 | res *Response,
104 | pmd *PaymentMethodDetails,
105 | err error,
106 | ) {
107 | u := fmt.Sprintf("v2/methods/%s", id)
108 |
109 | res, err = ms.client.get(ctx, u, options)
110 | if err != nil {
111 | return
112 | }
113 |
114 | if err = json.Unmarshal(res.content, &pmd); err != nil {
115 | return
116 | }
117 |
118 | return
119 | }
120 |
121 | // All retrieves all the payment methods enabled for your account/organization.
122 | //
123 | // See: https://docs.mollie.com/reference/list-all-methods
124 | func (ms *PaymentMethodsService) All(ctx context.Context, options *ListPaymentMethodsOptions) (
125 | res *Response,
126 | pm *PaymentMethodsList,
127 | err error,
128 | ) {
129 | return ms.list(ctx, "v2/methods/all", options)
130 | }
131 |
132 | // List retrieves all enabled payment methods.
133 | //
134 | // The results are not paginated.
135 | //
136 | // See: https://docs.mollie.com/reference/list-methods
137 | func (ms *PaymentMethodsService) List(ctx context.Context, options *ListPaymentMethodsOptions) (
138 | res *Response,
139 | pm *PaymentMethodsList,
140 | err error,
141 | ) {
142 | return ms.list(ctx, "v2/methods", options)
143 | }
144 |
145 | func (ms *PaymentMethodsService) list(ctx context.Context, uri string, options interface{}) (
146 | res *Response,
147 | pm *PaymentMethodsList,
148 | err error,
149 | ) {
150 | res, err = ms.client.get(ctx, uri, options)
151 | if err != nil {
152 | return
153 | }
154 |
155 | if err = json.Unmarshal(res.content, &pm); err != nil {
156 | return
157 | }
158 |
159 | return
160 | }
161 |
--------------------------------------------------------------------------------
/mollie/permissions.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | )
8 |
9 | // PermissionGrant defines supported permissions.
10 | type PermissionGrant string
11 |
12 | // Available permission grants.
13 | const (
14 | PaymentsRead PermissionGrant = "payments.read"
15 | PaymentsWrite PermissionGrant = "payments.write"
16 | RefundsRead PermissionGrant = "refunds.read"
17 | RefundsWrite PermissionGrant = "refunds.write"
18 | CustomersRead PermissionGrant = "customers.read"
19 | CustomersWrite PermissionGrant = "customers.write"
20 | MandatesRead PermissionGrant = "mandates.read"
21 | MandatesWrite PermissionGrant = "mandates.write"
22 | SubscriptionsRead PermissionGrant = "subscriptions.read"
23 | SubscriptionsWrite PermissionGrant = "subscriptions.write"
24 | ProfilesRead PermissionGrant = "profiles.read"
25 | ProfilesWrite PermissionGrant = "profiles.write"
26 | InvoicesRead PermissionGrant = "invoices.read"
27 | SettlementsRead PermissionGrant = "settlements.read"
28 | OrdersRead PermissionGrant = "orders.read"
29 | OrdersWrite PermissionGrant = "orders.write"
30 | ShipmentsRead PermissionGrant = "shipments.read"
31 | ShipmentsWrite PermissionGrant = "shipments.write"
32 | OrganizationsRead PermissionGrant = "organizations.read"
33 | OrganizationsWrite PermissionGrant = "organizations.write"
34 | OnboardingRead PermissionGrant = "onboarding.read"
35 | OnboardingWrite PermissionGrant = "onbording.write"
36 | PaymentLinksRead PermissionGrant = "payment-links.read"
37 | PaymentLinksWrite PermissionGrant = "payment-links.write"
38 | BalancesRead PermissionGrant = "balances.read"
39 | TerminalsRead PermissionGrant = "terminals.read"
40 | TerminalsWrite PermissionGrant = "terminals.write"
41 | )
42 |
43 | // Permission represents an action that
44 | // can be performed by any API actor.
45 | type Permission struct {
46 | Granted bool `json:"granted,omitempty"`
47 | Resource string `json:"resource,omitempty"`
48 | Description string `json:"description,omitempty"`
49 | ID PermissionGrant `json:"id,omitempty"`
50 | Links PermissionLinks `json:"_links,omitempty"`
51 | }
52 |
53 | // PermissionLinks contains URL objects that make
54 | // reference to an http address related to permissions.
55 | type PermissionLinks struct {
56 | Self *URL `json:"self,omitempty"`
57 | Documentation *URL `json:"documentation,omitempty"`
58 | }
59 |
60 | // PermissionsList lists all the permissions given to an
61 | // API actor.
62 | type PermissionsList struct {
63 | Count int `json:"count,omitempty"`
64 | Embedded struct {
65 | Permissions []*Permission `json:"permissions,omitempty"`
66 | } `json:"_embedded,omitempty"`
67 | Links PermissionLinks `json:"_links,omitempty"`
68 | }
69 |
70 | // PermissionsService operates over permission resources.
71 | type PermissionsService service
72 |
73 | // Get returns a permission by its id.
74 | //
75 | // See: https://docs.mollie.com/reference/get-permission
76 | func (ps *PermissionsService) Get(ctx context.Context, id PermissionGrant) (res *Response, p *Permission, err error) {
77 | res, err = ps.client.get(ctx, fmt.Sprintf("v2/permissions/%s", id), nil)
78 | if err != nil {
79 | return
80 | }
81 |
82 | if err = json.Unmarshal(res.content, &p); err != nil {
83 | return
84 | }
85 |
86 | return
87 | }
88 |
89 | // List retrieves all permissions available with the current app access token.
90 | // The list is not paginated.
91 | //
92 | // See: https://docs.mollie.com/reference/list-permissions
93 | func (ps *PermissionsService) List(ctx context.Context) (res *Response, pl *PermissionsList, err error) {
94 | res, err = ps.client.get(ctx, "v2/permissions", nil)
95 | if err != nil {
96 | return
97 | }
98 |
99 | if err = json.Unmarshal(res.content, &pl); err != nil {
100 | return
101 | }
102 |
103 | return
104 | }
105 |
--------------------------------------------------------------------------------
/mollie/permissions_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | "testing"
8 |
9 | "github.com/VictorAvelar/mollie-api-go/v4/testdata"
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | func TestPermissionsService_Get(t *testing.T) {
14 | setEnv()
15 | defer unsetEnv()
16 |
17 | type args struct {
18 | ctx context.Context
19 | permission PermissionGrant
20 | }
21 | cases := []struct {
22 | name string
23 | args args
24 | wantErr bool
25 | err error
26 | pre func()
27 | handler http.HandlerFunc
28 | }{
29 | {
30 | "get permission works as expected.",
31 | args{
32 | context.Background(),
33 | PaymentsRead,
34 | },
35 | false,
36 | nil,
37 | func() {
38 | tClient.WithAuthenticationValue("access_X12b31ggg23")
39 | },
40 | func(w http.ResponseWriter, r *http.Request) {
41 | testHeader(t, r, AuthHeader, "Bearer access_X12b31ggg23")
42 | testMethod(t, r, "GET")
43 | testQuery(t, r, "testmode=true")
44 |
45 | if _, ok := r.Header[AuthHeader]; !ok {
46 | w.WriteHeader(http.StatusUnauthorized)
47 | }
48 | _, _ = w.Write([]byte(testdata.GetPermissionsResponse))
49 | },
50 | },
51 | {
52 | "get permission, an error is returned from the server",
53 | args{
54 | context.Background(),
55 | PaymentsWrite,
56 | },
57 | true,
58 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
59 | noPre,
60 | errorHandler,
61 | },
62 | {
63 | "get permission, an error occurs when parsing json",
64 | args{
65 | context.Background(),
66 | PaymentsWrite,
67 | },
68 | true,
69 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
70 | noPre,
71 | encodingHandler,
72 | },
73 | {
74 | "get permission, invalid url when building request",
75 | args{
76 | context.Background(),
77 | PaymentsWrite,
78 | },
79 | true,
80 | errBadBaseURL,
81 | crashSrv,
82 | errorHandler,
83 | },
84 | }
85 |
86 | for _, c := range cases {
87 | setup()
88 | defer teardown()
89 |
90 | t.Run(c.name, func(t *testing.T) {
91 | c.pre()
92 | tMux.HandleFunc(fmt.Sprintf("/v2/permissions/%s", c.args.permission), c.handler)
93 |
94 | res, m, err := tClient.Permissions.Get(c.args.ctx, c.args.permission)
95 | if c.wantErr {
96 | assert.NotNil(t, err)
97 | assert.EqualError(t, err, c.err.Error())
98 | } else {
99 | assert.Nil(t, err)
100 | assert.IsType(t, &Permission{}, m)
101 | assert.IsType(t, &http.Response{}, res.Response)
102 | }
103 | })
104 | }
105 | }
106 |
107 | func TestPermissionsService_List(t *testing.T) {
108 | setEnv()
109 | defer unsetEnv()
110 |
111 | type args struct {
112 | ctx context.Context
113 | }
114 | cases := []struct {
115 | name string
116 | args args
117 | wantErr bool
118 | err error
119 | pre func()
120 | handler http.HandlerFunc
121 | }{
122 | {
123 | "get permission works as expected.",
124 | args{
125 | context.Background(),
126 | },
127 | false,
128 | nil,
129 | noPre,
130 | func(w http.ResponseWriter, r *http.Request) {
131 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
132 | testMethod(t, r, "GET")
133 |
134 | if _, ok := r.Header[AuthHeader]; !ok {
135 | w.WriteHeader(http.StatusUnauthorized)
136 | }
137 | _, _ = w.Write([]byte(testdata.GetPermissionsResponse))
138 | },
139 | },
140 | {
141 | "get permission, an error is returned from the server",
142 | args{
143 | context.Background(),
144 | },
145 | true,
146 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
147 | noPre,
148 | errorHandler,
149 | },
150 | {
151 | "get permission, an error occurs when parsing json",
152 | args{
153 | context.Background(),
154 | },
155 | true,
156 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
157 | noPre,
158 | encodingHandler,
159 | },
160 | {
161 | "get permission, invalid url when building request",
162 | args{
163 | context.Background(),
164 | },
165 | true,
166 | errBadBaseURL,
167 | crashSrv,
168 | errorHandler,
169 | },
170 | }
171 |
172 | setEnv()
173 | defer unsetEnv()
174 | for _, c := range cases {
175 | setup()
176 | defer teardown()
177 | t.Run(c.name, func(t *testing.T) {
178 | c.pre()
179 | tMux.HandleFunc("/v2/permissions", c.handler)
180 |
181 | res, m, err := tClient.Permissions.List(c.args.ctx)
182 | if c.wantErr {
183 | assert.NotNil(t, err)
184 | assert.EqualError(t, err, c.err.Error())
185 | } else {
186 | assert.Nil(t, err)
187 | assert.IsType(t, &PermissionsList{}, m)
188 | assert.IsType(t, &http.Response{}, res.Response)
189 | }
190 | })
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/mollie/shipments.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "time"
8 | )
9 |
10 | // CreateShipment contains information required to create a new shipment.
11 | type CreateShipment struct {
12 | Lines []*OrderLine `json:"lines,omitempty"`
13 | Tracking *ShipmentTracking `json:"tracking,omitempty"`
14 | ShipmentAccessTokenFields
15 | }
16 |
17 | // ShipmentAccessTokenFields describes the fields available when using an access token.
18 | type ShipmentAccessTokenFields struct {
19 | Testmode bool `json:"testmode,omitempty"`
20 | }
21 |
22 | // Shipment contains information about a user service/product delivery and
23 | // is used in the figurative sense here.
24 | // It can also mean that a service was provided or digital content was delivered.
25 | type Shipment struct {
26 | Resource string `json:"resource,omitempty"`
27 | ID string `json:"id,omitempty"`
28 | OrderID string `json:"orderId,omitempty"`
29 | CreatedAt *time.Time `json:"createdAt,omitempty"`
30 | Tracking *ShipmentTracking `json:"tracking,omitempty"`
31 | Lines []*OrderLine `json:"lines,omitempty"`
32 | Links ShipmentLinks `json:"_links,omitempty"`
33 | ShipmentAccessTokenFields
34 | }
35 |
36 | // UpdateShipment contains information required to update a shipment.
37 | type UpdateShipment struct {
38 | Tracking *ShipmentTracking `json:"tracking,omitempty"`
39 | ShipmentAccessTokenFields
40 | }
41 |
42 | // ShipmentTracking contains shipment tracking details.
43 | type ShipmentTracking struct {
44 | Carrier string `json:"carrier,omitempty"`
45 | Code string `json:"code,omitempty"`
46 | URL string `json:"url,omitempty"`
47 | }
48 |
49 | // ShipmentLinks contains URL objects with shipment relevant
50 | // information for the user.
51 | type ShipmentLinks struct {
52 | Self *URL `json:"self,omitempty"`
53 | Order *URL `json:"order,omitempty"`
54 | Documentation *URL `json:"documentation,omitempty"`
55 | }
56 |
57 | // ShipmentsList describes how a list of payments will be retrieved by Mollie.
58 | type ShipmentsList struct {
59 | Count int `json:"count,omitempty"`
60 | Embedded struct {
61 | Shipments []Shipment
62 | } `json:"_embedded,omitempty"`
63 | Links PaginationLinks `json:"_links,omitempty"`
64 | }
65 |
66 | // ShipmentsService operates on shipments endpoints.
67 | type ShipmentsService service
68 |
69 | // Get retrieves a single shipment and the order lines shipped by a shipment’s ID.
70 | //
71 | // See: https://docs.mollie.com/reference/get-shipment#
72 | func (ss *ShipmentsService) Get(ctx context.Context, order string, shipment string) (
73 | res *Response,
74 | s *Shipment,
75 | err error,
76 | ) {
77 | u := fmt.Sprintf("v2/orders/%s/shipments/%s", order, shipment)
78 |
79 | res, err = ss.client.get(ctx, u, nil)
80 | if err != nil {
81 | return
82 | }
83 |
84 | if err = json.Unmarshal(res.content, &s); err != nil {
85 | return
86 | }
87 |
88 | return
89 | }
90 |
91 | // Create can be used to ship order lines.
92 | //
93 | // See: https://docs.mollie.com/reference/create-shipment
94 | func (ss *ShipmentsService) Create(ctx context.Context, order string, cs CreateShipment) (
95 | res *Response,
96 | s *Shipment,
97 | err error,
98 | ) {
99 | uri := fmt.Sprintf("v2/orders/%s/shipments", order)
100 |
101 | if ss.client.HasAccessToken() && ss.client.config.testing {
102 | cs.Testmode = true
103 | }
104 |
105 | res, err = ss.client.post(ctx, uri, cs, nil)
106 | if err != nil {
107 | return
108 | }
109 |
110 | if err = json.Unmarshal(res.content, &s); err != nil {
111 | return
112 | }
113 |
114 | return
115 | }
116 |
117 | // List retrieves all shipments for an order.
118 | //
119 | // See: https://docs.mollie.com/reference/list-shipments
120 | func (ss *ShipmentsService) List(ctx context.Context, order string) (res *Response, sl *ShipmentsList, err error) {
121 | u := fmt.Sprintf("v2/orders/%s/shipments", order)
122 |
123 | res, err = ss.client.get(ctx, u, nil)
124 | if err != nil {
125 | return
126 | }
127 |
128 | if err = json.Unmarshal(res.content, &sl); err != nil {
129 | return
130 | }
131 |
132 | return
133 | }
134 |
135 | // Update can be used to update the tracking information of a shipment
136 | //
137 | // See: https://docs.mollie.com/reference/update-shipment
138 | func (ss *ShipmentsService) Update(ctx context.Context, order string, shipment string, us UpdateShipment) (
139 | res *Response,
140 | s *Shipment,
141 | err error,
142 | ) {
143 | u := fmt.Sprintf("v2/orders/%s/shipments/%s", order, shipment)
144 |
145 | res, err = ss.client.patch(ctx, u, us)
146 | if err != nil {
147 | return
148 | }
149 |
150 | if err = json.Unmarshal(res.content, &s); err != nil {
151 | return
152 | }
153 |
154 | return
155 | }
156 |
--------------------------------------------------------------------------------
/mollie/terminals.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "time"
8 | )
9 |
10 | // TerminalStatus is the status of the terminal, which is a read-only value determined by Mollie.
11 | type TerminalStatus string
12 |
13 | // Possible terminal statuses.
14 | const (
15 | TerminalPending TerminalStatus = "pending"
16 | TerminalActive TerminalStatus = "active"
17 | TerminalInactive TerminalStatus = "inactive"
18 | )
19 |
20 | // Terminal symbolizes a physical device to receive payments.
21 | type Terminal struct {
22 | ID string `json:"id,omitempty"`
23 | Resource string `json:"resource,omitempty"`
24 | ProfileID string `json:"profileID,omitempty"`
25 | Brand string `json:"brand,omitempty"`
26 | Model string `json:"model,omitempty"`
27 | SerialNumber string `json:"serialNumber,omitempty"`
28 | Currency string `json:"currency,omitempty"`
29 | Description string `json:"description,omitempty"`
30 | CreatedAt *time.Time `json:"createdAt,omitempty"`
31 | UpdatedAt *time.Time `json:"updatedAt,omitempty"`
32 | Status TerminalStatus `json:"status,omitempty"`
33 | Links PaginationLinks `json:"_links,omitempty"`
34 | }
35 |
36 | // ListTerminalsOptions holds query string parameters valid for terminals lists.
37 | //
38 | // ProfileID and TestMode are valid only when using access tokens.
39 | type ListTerminalsOptions struct {
40 | Testmode bool `url:"testMode,omitempty"`
41 | Limit int `url:"limit,omitempty"`
42 | From string `url:"from,omitempty"`
43 | ProfileID string `url:"profileID,omitempty"`
44 | }
45 |
46 | // TerminalList describes the response for terminals list endpoints.
47 | type TerminalList struct {
48 | Count int `json:"count,omitempty"`
49 | Embedded struct {
50 | Terminals []*Terminal `json:"terminals,omitempty"`
51 | } `json:"_embedded,omitempty"`
52 | Links PaginationLinks `json:"_links,omitempty"`
53 | }
54 |
55 | // TerminalsService operates over terminals resource.
56 | type TerminalsService service
57 |
58 | // Get terminal retrieves a single terminal object by its terminal ID.
59 | func (ts *TerminalsService) Get(ctx context.Context, id string) (res *Response, t *Terminal, err error) {
60 | res, err = ts.client.get(ctx, fmt.Sprintf("v2/terminals/%s", id), nil)
61 | if err != nil {
62 | return
63 | }
64 |
65 | if err = json.Unmarshal(res.content, &t); err != nil {
66 | return
67 | }
68 |
69 | return
70 | }
71 |
72 | // List retrieves a list of terminals symbolizing the physical devices to receive payments.
73 | func (ts *TerminalsService) List(ctx context.Context, options *ListTerminalsOptions) (
74 | res *Response,
75 | tl *TerminalList,
76 | err error,
77 | ) {
78 | if ts.client.HasAccessToken() && ts.client.config.testing {
79 | options.Testmode = true
80 | }
81 |
82 | res, err = ts.client.get(ctx, "v2/terminals", options)
83 | if err != nil {
84 | return
85 | }
86 |
87 | if err = json.Unmarshal(res.content, &tl); err != nil {
88 | return
89 | }
90 |
91 | return
92 | }
93 |
--------------------------------------------------------------------------------
/mollie/terminals_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | "testing"
8 |
9 | "github.com/VictorAvelar/mollie-api-go/v4/testdata"
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | func TestTerminalsService_Get(t *testing.T) {
14 | setEnv()
15 | defer unsetEnv()
16 |
17 | type args struct {
18 | ctx context.Context
19 | id string
20 | }
21 |
22 | cases := []struct {
23 | name string
24 | args args
25 | wantErr bool
26 | err error
27 | want string
28 | pre func()
29 | handler http.HandlerFunc
30 | }{
31 | {
32 | "get terminal correctly",
33 | args{
34 | context.Background(),
35 | "term_7MgL4wea46qkRcoTZjWEH",
36 | },
37 | false,
38 | nil,
39 | testdata.GetTerminalResponse,
40 | noPre,
41 | func(w http.ResponseWriter, r *http.Request) {
42 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
43 | testMethod(t, r, "GET")
44 |
45 | if _, ok := r.Header[AuthHeader]; !ok {
46 | w.WriteHeader(http.StatusUnauthorized)
47 | }
48 | _, _ = w.Write([]byte(testdata.GetTerminalResponse))
49 | },
50 | },
51 | {
52 | "get terminal, an error is returned from the server",
53 | args{
54 | context.Background(),
55 | "term_7MgL4wea46qkRcoTZjWEH",
56 | },
57 | true,
58 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
59 | "",
60 | noPre,
61 | errorHandler,
62 | },
63 | {
64 | "get terminal, an error occurs when parsing json",
65 | args{
66 | context.Background(),
67 | "term_7MgL4wea46qkRcoTZjWEH",
68 | },
69 | true,
70 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
71 | "",
72 | noPre,
73 | encodingHandler,
74 | },
75 | {
76 | "get terminal, invalid url when building request",
77 | args{
78 | context.Background(),
79 | "term_7MgL4wea46qkRcoTZjWEH",
80 | },
81 | true,
82 | errBadBaseURL,
83 | "",
84 | crashSrv,
85 | errorHandler,
86 | },
87 | }
88 |
89 | for _, c := range cases {
90 | setup()
91 | defer teardown()
92 |
93 | t.Run(c.name, func(t *testing.T) {
94 | c.pre()
95 | tMux.HandleFunc(fmt.Sprintf("/v2/terminals/%s", c.args.id), c.handler)
96 |
97 | res, m, err := tClient.Terminals.Get(c.args.ctx, c.args.id)
98 | if c.wantErr {
99 | assert.NotNil(t, err)
100 | assert.EqualError(t, err, c.err.Error())
101 | } else {
102 | assert.Nil(t, err)
103 | assert.IsType(t, &Terminal{}, m)
104 | assert.IsType(t, &http.Response{}, res.Response)
105 | }
106 | })
107 | }
108 | }
109 |
110 | func TestTerminalsService_List(t *testing.T) {
111 | setEnv()
112 | defer unsetEnv()
113 |
114 | type args struct {
115 | ctx context.Context
116 | options *ListTerminalsOptions
117 | }
118 |
119 | cases := []struct {
120 | name string
121 | args args
122 | wantErr bool
123 | err error
124 | want string
125 | pre func()
126 | handler http.HandlerFunc
127 | }{
128 | {
129 | "list terminals correctly",
130 | args{
131 | context.Background(),
132 | &ListTerminalsOptions{},
133 | },
134 | false,
135 | nil,
136 | testdata.GetTerminalResponse,
137 | noPre,
138 | func(w http.ResponseWriter, r *http.Request) {
139 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
140 | testMethod(t, r, "GET")
141 |
142 | if _, ok := r.Header[AuthHeader]; !ok {
143 | w.WriteHeader(http.StatusUnauthorized)
144 | }
145 | _, _ = w.Write([]byte(testdata.ListTerminalsResponse))
146 | },
147 | },
148 | {
149 | "list terminals correctly with an access token",
150 | args{
151 | context.Background(),
152 | &ListTerminalsOptions{},
153 | },
154 | false,
155 | nil,
156 | testdata.GetTerminalResponse,
157 | setAccessToken,
158 | func(w http.ResponseWriter, r *http.Request) {
159 | testHeader(t, r, AuthHeader, "Bearer access_token_test")
160 | testMethod(t, r, "GET")
161 |
162 | if _, ok := r.Header[AuthHeader]; !ok {
163 | w.WriteHeader(http.StatusUnauthorized)
164 | }
165 | _, _ = w.Write([]byte(testdata.ListTerminalsResponse))
166 | },
167 | },
168 | {
169 | "get terminals list, an error is returned from the server",
170 | args{
171 | context.Background(),
172 | nil,
173 | },
174 | true,
175 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
176 | "",
177 | noPre,
178 | errorHandler,
179 | },
180 | {
181 | "get terminals list, an error occurs when parsing json",
182 | args{
183 | context.Background(),
184 | nil,
185 | },
186 | true,
187 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
188 | "",
189 | noPre,
190 | encodingHandler,
191 | },
192 | {
193 | "get terminals list, invalid url when building request",
194 | args{
195 | context.Background(),
196 | nil,
197 | },
198 | true,
199 | errBadBaseURL,
200 | "",
201 | crashSrv,
202 | errorHandler,
203 | },
204 | }
205 |
206 | for _, c := range cases {
207 | setup()
208 | defer teardown()
209 |
210 | t.Run(c.name, func(t *testing.T) {
211 | c.pre()
212 | tMux.HandleFunc("/v2/terminals", c.handler)
213 |
214 | res, m, err := tClient.Terminals.List(c.args.ctx, c.args.options)
215 | if c.wantErr {
216 | assert.NotNil(t, err)
217 | assert.EqualError(t, err, c.err.Error())
218 | } else {
219 | assert.Nil(t, err)
220 | assert.IsType(t, &TerminalList{}, m)
221 | assert.IsType(t, &http.Response{}, res.Response)
222 | }
223 | })
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/mollie/vouchers.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | // VoucherIssuer represents the issuer of a voucher.
4 | type VoucherIssuer string
5 |
6 | // List of known voucher issuers.
7 | const (
8 | EdenredBelgiumCadeauVoucher VoucherIssuer = "edenred-belgium-cadeau"
9 | EdenredBelgiumEcoVoucher VoucherIssuer = "edenred-belgium-eco"
10 | EdenredBelgiumMealVoucher VoucherIssuer = "edenred-belgium-meal"
11 | EdenredBelgiumSportsVoucher VoucherIssuer = "edenred-belgium-sports"
12 | EdenredBelgiumAdditionalVoucher VoucherIssuer = "edenred-belgium-additional"
13 | EdenredBelgiumConsumeVoucher VoucherIssuer = "edenred-belgium-consume"
14 | MonizzeCadeauVoucher VoucherIssuer = "monizze-cadeau"
15 | MonizzeEcoVoucher VoucherIssuer = "monizze-eco"
16 | MonizzeMealVoucher VoucherIssuer = "monizze-meal"
17 | PluxeeCadeauVoucher VoucherIssuer = "sodexo-cadeau"
18 | PluxeeEcoVoucher VoucherIssuer = "sodexo-ecopass"
19 | PluxeeLunchVoucher VoucherIssuer = "sodexo-lunchpass"
20 | )
21 |
22 | // VoucherIssuerEnabled describes the response of a voucher enable operation.
23 | type VoucherIssuerEnabled struct {
24 | ID string `json:"id,omitempty"`
25 | Description string `json:"description,omitempty"`
26 | Status IssuerStatus `json:"status,omitempty"`
27 | Contractor VoucherContractor `json:"contractor,omitempty"`
28 | Links VoucherLinks `json:"_links,omitempty"`
29 | }
30 |
31 | // VoucherLinks are links embedded when a voucher is enabled.
32 | type VoucherLinks struct {
33 | Self *URL `json:"self,omitempty"`
34 | Documentation *URL `json:"documentation,omitempty"`
35 | }
36 |
37 | // VoucherContractor represents a contractor for a voucher.
38 | type VoucherContractor struct {
39 | ID string `json:"id,omitempty"`
40 | Name string `json:"name,omitempty"`
41 | ContractorID string `json:"contractorId,omitempty"`
42 | }
43 |
--------------------------------------------------------------------------------
/mollie/wallets.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | )
7 |
8 | // Wallet describes the wallet types that Mollie supports.
9 | type Wallet string
10 |
11 | // Available wallet types.
12 | const (
13 | ApplePayWallet Wallet = "applepay"
14 | )
15 |
16 | // WalletsService operates over the resources described
17 | // in Mollie's wallets API endpoints section.
18 | type WalletsService service
19 |
20 | // ApplePaymentSession contains information about an Apple pay session.
21 | type ApplePaymentSession struct {
22 | EpochTimestamp int `json:"epochTimestamp,omitempty"`
23 | ExpiresAt int `json:"expiresAt,omitempty"`
24 | MerchantSessionID string `json:"merchantSessionIdentifier,omitempty"`
25 | Nonce string `json:"nonce,omitempty"`
26 | MerchantID string `json:"merchantIdentified,omitempty"`
27 | DomainName string `json:"domainName,omitempty"`
28 | DisplayName string `json:"displayName,omitempty"`
29 | Signature string `json:"signature,omitempty"`
30 | }
31 |
32 | // ApplePaymentSessionRequest contains the body parameters for requesting
33 | // a valid PaymentSession from Apple.
34 | type ApplePaymentSessionRequest struct {
35 | Domain string `json:"domain,omitempty"`
36 | ValidationURL string `json:"validationUrl,omitempty"`
37 | }
38 |
39 | // ApplePaymentSession returns an Apple Payment Session object valid for one transaction.
40 | //
41 | // See: https://docs.mollie.com/reference/request-apple-pay-payment-session
42 | func (ms *WalletsService) ApplePaymentSession(ctx context.Context, asr *ApplePaymentSessionRequest) (
43 | res *Response,
44 | aps *ApplePaymentSession,
45 | err error,
46 | ) {
47 | u := "v2/wallets/applepay/sessions"
48 |
49 | res, err = ms.client.post(ctx, u, asr, nil)
50 | if err != nil {
51 | return
52 | }
53 |
54 | if err = json.Unmarshal(res.content, &aps); err != nil {
55 | return
56 | }
57 |
58 | return
59 | }
60 |
--------------------------------------------------------------------------------
/mollie/wallets_test.go:
--------------------------------------------------------------------------------
1 | package mollie
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | "testing"
8 |
9 | "github.com/VictorAvelar/mollie-api-go/v4/testdata"
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | func TestMiscellaneousService_ApplePaymentSession(t *testing.T) {
14 | setEnv()
15 | defer unsetEnv()
16 |
17 | type args struct {
18 | ctx context.Context
19 | appleSess *ApplePaymentSessionRequest
20 | }
21 |
22 | cases := []struct {
23 | name string
24 | args args
25 | wantErr bool
26 | err error
27 | pre func()
28 | handler http.HandlerFunc
29 | }{
30 | {
31 | "get apple payment session works as expected.",
32 | args{
33 | context.Background(),
34 | &ApplePaymentSessionRequest{
35 | Domain: "https://example.com",
36 | },
37 | },
38 | false,
39 | nil,
40 | noPre,
41 | func(w http.ResponseWriter, r *http.Request) {
42 | testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
43 | testMethod(t, r, "POST")
44 |
45 | if _, ok := r.Header[AuthHeader]; !ok {
46 | w.WriteHeader(http.StatusUnauthorized)
47 | }
48 | _, _ = w.Write([]byte(testdata.ListMethodsResponse))
49 | },
50 | },
51 | {
52 | "get apple payment session, an error is returned from the server",
53 | args{
54 | context.Background(),
55 | nil,
56 | },
57 | true,
58 | fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request"),
59 | noPre,
60 | errorHandler,
61 | },
62 | {
63 | "get apple payment session, an error occurs when parsing json",
64 | args{
65 | context.Background(),
66 | nil,
67 | },
68 | true,
69 | fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
70 | noPre,
71 | encodingHandler,
72 | },
73 | {
74 | "get apple payment session, invalid url when building request",
75 | args{
76 | context.Background(),
77 | nil,
78 | },
79 | true,
80 | errBadBaseURL,
81 | crashSrv,
82 | errorHandler,
83 | },
84 | }
85 |
86 | for _, c := range cases {
87 | setup()
88 | defer teardown()
89 |
90 | t.Run(c.name, func(t *testing.T) {
91 | c.pre()
92 | tMux.HandleFunc("/v2/wallets/applepay/sessions", c.handler)
93 |
94 | res, m, err := tClient.Wallets.ApplePaymentSession(c.args.ctx, c.args.appleSess)
95 | if c.wantErr {
96 | assert.NotNil(t, err)
97 | assert.EqualError(t, err, c.err.Error())
98 | } else {
99 | assert.Nil(t, err)
100 | assert.IsType(t, &ApplePaymentSession{}, m)
101 | assert.IsType(t, &http.Response{}, res.Response)
102 | }
103 | })
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/pkg/connect/oauth2.go:
--------------------------------------------------------------------------------
1 | package connect
2 |
3 | import (
4 | "golang.org/x/oauth2"
5 | )
6 |
7 | // Mollie's Oauth2 server URLs.
8 | const (
9 | authURL = "https://www.mollie.com/oauth2/authorize"
10 | tokensURL = "https://api.mollie.com/oauth2/tokens" //#nosec G101 -- This is the url used to retrieve oauth2 tokens.
11 | )
12 |
13 | // OauthEndpoint is Mollies's OAuth 2.0 endpoint.
14 | func OauthEndpoint() *oauth2.Endpoint {
15 | return &oauth2.Endpoint{
16 | AuthURL: authURL,
17 | TokenURL: tokensURL,
18 | AuthStyle: 0,
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/pkg/connect/oauth2_test.go:
--------------------------------------------------------------------------------
1 | package connect
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 | "golang.org/x/oauth2"
8 | )
9 |
10 | func Test_Endpoint(t *testing.T) {
11 | ept := OauthEndpoint()
12 | require.Equal(t, ept.AuthURL, authURL)
13 | require.Equal(t, ept.AuthStyle, oauth2.AuthStyleAutoDetect)
14 | require.Equal(t, ept.TokenURL, tokensURL)
15 | }
16 |
--------------------------------------------------------------------------------
/pkg/idempotency/contract.go:
--------------------------------------------------------------------------------
1 | package idempotency
2 |
3 | // KeyGenerator describes the service in charge
4 | // of generating a unique idempotency key to be passed in
5 | // POST requests to ensure operation uniqueness.
6 | //
7 | // See: https://docs.mollie.com/overview/api-idempotency
8 | type KeyGenerator interface {
9 | // Generate encapsulates the logic to return a string representation of
10 | // a unique idempotency key.
11 | Generate() string
12 | }
13 |
--------------------------------------------------------------------------------
/pkg/idempotency/doc.go:
--------------------------------------------------------------------------------
1 | // Package idempotency contains the services in charge
2 | // of generating a unique keys to be passed in
3 | // POST requests to ensure operation uniqueness.
4 | //
5 | // See: https://docs.mollie.com/overview/api-idempotency
6 | //
7 | // The std generator uses google's uuid library to return a new uuid
8 | // as unique idempotency key.
9 | //
10 | // You can build your own generator and pass it to the library by
11 | // implementing the KeyGenerator interface.
12 | package idempotency
13 |
--------------------------------------------------------------------------------
/pkg/idempotency/nop.go:
--------------------------------------------------------------------------------
1 | package idempotency
2 |
3 | // NOpIdempotencyGenerator is a dummy implementation of the
4 | // IdempotencyKeyGenerator interface.
5 | //
6 | // Good for testing or when a predictable result is required.
7 | type nOpIdempotencyGenerator struct {
8 | expected string
9 | }
10 |
11 | // TestKeyExpected is the default value for the NOpGenerator.
12 | const (
13 | TestKeyExpected = "test_ikg_key"
14 | )
15 |
16 | // Generate encapsulates the logic to return a string representation of
17 | // a unique idempotency key.
18 | func (nopIKG nOpIdempotencyGenerator) Generate() string {
19 | return nopIKG.expected
20 | }
21 |
22 | // NewNopGenerator returns a dummy implementation of the
23 | // IdempotencyKeyGenerator interface.
24 | //
25 | // Good for testing or when a predictable result is required.
26 | //
27 | // If exp is an empty string, then TestKeyExpected is used as
28 | // default value for the NOpGenerator.
29 | func NewNopGenerator(exp string) KeyGenerator {
30 | if exp == "" {
31 | exp = TestKeyExpected
32 | }
33 |
34 | return nOpIdempotencyGenerator{
35 | expected: exp,
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/pkg/idempotency/nop_test.go:
--------------------------------------------------------------------------------
1 | package idempotency
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestNopGenerator(t *testing.T) {
10 | assert.Implements(t, new(KeyGenerator), NewNopGenerator("dummy"))
11 | }
12 |
13 | func TestGenerateDefault(t *testing.T) {
14 | g := NewNopGenerator("")
15 |
16 | assert.Equal(t, TestKeyExpected, g.Generate())
17 | }
18 |
19 | func TestGenerateNonDefault(t *testing.T) {
20 | g := NewNopGenerator("testing")
21 |
22 | assert.NotEqual(t, TestKeyExpected, g.Generate())
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/idempotency/std.go:
--------------------------------------------------------------------------------
1 | package idempotency
2 |
3 | import "github.com/google/uuid"
4 |
5 | type stdGenerator struct{}
6 |
7 | // Generate encapsulates the logic to return a string representation of
8 | // a unique idempotency key.
9 | //
10 | // A string representation of a v4 uuid for this implementation.
11 | func (ikg stdGenerator) Generate() string {
12 | return uuid.New().String()
13 | }
14 |
15 | // NewStdGenerator returns an standard and common way of generating
16 | // idempotency unique keys.
17 | func NewStdGenerator() KeyGenerator {
18 | return new(stdGenerator)
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/idempotency/std_test.go:
--------------------------------------------------------------------------------
1 | package idempotency
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestNewStdGenerator(t *testing.T) {
10 | assert.Implements(t, new(KeyGenerator), NewStdGenerator())
11 | }
12 |
13 | func TestStandardGenerator(t *testing.T) {
14 | key := NewStdGenerator().Generate()
15 |
16 | assert.Len(t, key, 36)
17 |
18 | assert.NotEqual(t, key, NewStdGenerator().Generate())
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/pagination/doc.go:
--------------------------------------------------------------------------------
1 | // Package pagination provides utilities to handle pagination in API responses.
2 | //
3 | // Pagination is a common feature in APIs that allows retrieval of large datasets
4 | // in smaller chunks, enhancing performance and resource usage. This package aims
5 | // to simplify pagination-related tasks by providing helpful functions.
6 | package pagination
7 |
--------------------------------------------------------------------------------
/pkg/pagination/pagination.go:
--------------------------------------------------------------------------------
1 | package pagination
2 |
3 | import (
4 | "net/url"
5 | )
6 |
7 | // ExtractFromQueryParam extracts the lastID from the given URI, which is assumed to be a URL with query parameters.
8 | // It specifically looks for a query parameter named 'from' and returns its value as a string.
9 | // If the URI cannot be parsed or the query parameter is not found, it returns an empty string and the encountered
10 | // error.
11 | func ExtractFromQueryParam(uri string) (lastID string, err error) {
12 | const from = "from"
13 |
14 | return parseURIAndReturnQueryParam(uri, from)
15 | }
16 |
17 | func parseURIAndReturnQueryParam(uri string, param string) (val string, err error) {
18 | u, err := url.Parse(uri)
19 | if err != nil {
20 | return "", err
21 | }
22 |
23 | v := u.Query().Get(param)
24 |
25 | return v, nil
26 | }
27 |
--------------------------------------------------------------------------------
/pkg/pagination/pagination_test.go:
--------------------------------------------------------------------------------
1 | package pagination
2 |
3 | import "testing"
4 |
5 | func TestExtractFromQueryParam(t *testing.T) {
6 | type args struct {
7 | uri string
8 | }
9 | tests := []struct {
10 | name string
11 | args args
12 | wantLastID string
13 | wantErr bool
14 | }{
15 | {
16 | name: "test extracts correct parameter",
17 | args: args{
18 | "https://api.mollie.com/v2/payments?from=tr_EkceGSH8Ga&limit=5",
19 | },
20 | wantLastID: "tr_EkceGSH8Ga",
21 | wantErr: false,
22 | },
23 | {
24 | name: "test wrong url error parameter",
25 | args: args{
26 | "h%%s12",
27 | },
28 | wantLastID: "",
29 | wantErr: true,
30 | },
31 | }
32 | for _, tt := range tests {
33 | t.Run(tt.name, func(t *testing.T) {
34 | gotLastID, err := ExtractFromQueryParam(tt.args.uri)
35 | t.Log(err)
36 | if (err != nil) != tt.wantErr {
37 | t.Errorf("ExtractFromQueryParam() error = %v, wantErr %v", err, tt.wantErr)
38 | return
39 | }
40 | if gotLastID != tt.wantLastID {
41 | t.Errorf("ExtractFromQueryParam() = %v, want %v", gotLastID, tt.wantLastID)
42 | }
43 | })
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/testdata/captures.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // ListCapturesResponse example
4 | const ListCapturesResponse = `{
5 | "_embedded": {
6 | "captures": [
7 | {
8 | "resource": "capture",
9 | "id": "cpt_4qqhO89gsT",
10 | "mode": "live",
11 | "amount": {
12 | "value": "1027.99",
13 | "currency": "EUR"
14 | },
15 | "settlementAmount": {
16 | "value": "399.00",
17 | "currency": "EUR"
18 | },
19 | "paymentId": "tr_WDqYK6vllg",
20 | "shipmentId": "shp_3wmsgCJN4U",
21 | "settlementId": "stl_jDk30akdN",
22 | "createdAt": "2018-08-02T09:29:56+00:00",
23 | "_links": {
24 | "self": {
25 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg/captures/cpt_4qqhO89gsT",
26 | "type": "application/hal+json"
27 | },
28 | "payment": {
29 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg",
30 | "type": "application/hal+json"
31 | },
32 | "shipment": {
33 | "href": "https://api.mollie.com/v2/orders/ord_8wmqcHMN4U/shipments/shp_3wmsgCJN4U",
34 | "type": "application/hal+json"
35 | },
36 | "settlement": {
37 | "href": "https://api.mollie.com/v2/settlements/stl_jDk30akdN",
38 | "type": "application/hal+json"
39 | },
40 | "documentation": {
41 | "href": "https://docs.mollie.com/reference/captures-api/get-capture",
42 | "type": "text/html"
43 | }
44 | }
45 | }
46 | ]
47 | },
48 | "count": 1,
49 | "_links": {
50 | "documentation": {
51 | "href": "https://docs.mollie.com/reference/captures-api/list-captures",
52 | "type": "text/html"
53 | },
54 | "self": {
55 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg/captures?limit=50",
56 | "type": "application/hal+json"
57 | },
58 | "previous": null,
59 | "next": null
60 | }
61 | }`
62 |
63 | // GetCaptureResponse example
64 | const GetCaptureResponse = `{
65 | "resource": "capture",
66 | "id": "cpt_4qqhO89gsT",
67 | "mode": "live",
68 | "amount": {
69 | "value": "1027.99",
70 | "currency": "EUR"
71 | },
72 | "settlementAmount": {
73 | "value": "399.00",
74 | "currency": "EUR"
75 | },
76 | "paymentId": "tr_WDqYK6vllg",
77 | "shipmentId": "shp_3wmsgCJN4U",
78 | "settlementId": "stl_jDk30akdN",
79 | "createdAt": "2018-08-02T09:29:56+00:00",
80 | "_links": {
81 | "self": {
82 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg/captures/cpt_4qqhO89gsT",
83 | "type": "application/hal+json"
84 | },
85 | "payment": {
86 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg",
87 | "type": "application/hal+json"
88 | },
89 | "shipment": {
90 | "href": "https://api.mollie.com/v2/orders/ord_8wmqcHMN4U/shipments/shp_3wmsgCJN4U",
91 | "type": "application/hal+json"
92 | },
93 | "settlement": {
94 | "href": "https://api.mollie.com/v2/settlements/stl_jDk30akdN",
95 | "type": "application/hal+json"
96 | },
97 | "documentation": {
98 | "href": "https://docs.mollie.com/reference/captures-api/get-capture",
99 | "type": "text/html"
100 | }
101 | }
102 | }`
103 |
104 | // CreateCaptureResponse example.
105 | const CreateCaptureResponse = `{
106 | "resource": "capture",
107 | "id": "cpt_mNepDkEtco6ah3QNPUGYH",
108 | "mode": "live",
109 | "amount": {
110 | "value": "35.95",
111 | "currency": "EUR"
112 | },
113 | "paymentId": "tr_WDqYK6vllg",
114 | "createdAt": "2018-08-02T09:29:56+00:00",
115 | "description": "Capture for cart #12345",
116 | "_links": {
117 | "self": {
118 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg/captures/cpt_mNepDkEtco6ah3QNPUGYH",
119 | "type": "application/hal+json"
120 | },
121 | "payment": {
122 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg",
123 | "type": "application/hal+json"
124 | },
125 | "documentation": {
126 | "href": "https://docs.mollie.com/reference/captures-api/create-capture",
127 | "type": "text/html"
128 | }
129 | }
130 | }`
131 |
132 | // CreateCaptureWithAccessTokenResponse example.
133 | const CreateCaptureWithAccessTokenResponse = `{
134 | "resource": "capture",
135 | "id": "cpt_mNepDkEtco6ah3QNPUGYH",
136 | "mode": "live",
137 | "amount": {
138 | "value": "35.95",
139 | "currency": "EUR"
140 | },
141 | "paymentId": "tr_WDqYK6vllg",
142 | "createdAt": "2018-08-02T09:29:56+00:00",
143 | "description": "Capture for cart #12345",
144 | "testmode": true,
145 | "_links": {
146 | "self": {
147 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg/captures/cpt_mNepDkEtco6ah3QNPUGYH",
148 | "type": "application/hal+json"
149 | },
150 | "payment": {
151 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg",
152 | "type": "application/hal+json"
153 | },
154 | "documentation": {
155 | "href": "https://docs.mollie.com/reference/captures-api/create-capture",
156 | "type": "text/html"
157 | }
158 | }
159 | }`
160 |
--------------------------------------------------------------------------------
/testdata/chargebacks.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | const (
4 | GetChargebackResponse = `{
5 | "resource": "chargeback",
6 | "id": "chb_n9z0tp",
7 | "amount": {
8 | "currency": "USD",
9 | "value": "43.38"
10 | },
11 | "settlementAmount": {
12 | "currency": "EUR",
13 | "value": "-35.07"
14 | },
15 | "createdAt": "2018-03-14T17:00:52.0Z",
16 | "reason": {
17 | "code": "AC01",
18 | "description": "Account identifier incorrect (i.e. invalid IBAN)"
19 | },
20 | "reversedAt": null,
21 | "paymentId": "tr_WDqYK6vllg",
22 | "_links": {
23 | "self": {
24 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg/chargebacks/chb_n9z0tp",
25 | "type": "application/hal+json"
26 | },
27 | "payment": {
28 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg",
29 | "type": "application/hal+json"
30 | },
31 | "documentation": {
32 | "href": "https://docs.mollie.com/reference/chargebacks-api/get-payment-chargeback",
33 | "type": "text/html"
34 | }
35 | }
36 | }`
37 | ListChargebacksResponse = `{
38 | "count": 3,
39 | "_embedded": {
40 | "chargebacks": [
41 | {
42 | "resource": "chargeback",
43 | "id": "chb_n9z0tp",
44 | "amount": {
45 | "currency": "USD",
46 | "value": "43.38"
47 | },
48 | "settlementAmount": {
49 | "currency": "EUR",
50 | "value": "35.07"
51 | },
52 | "createdAt": "2018-03-14T17:00:52.0Z",
53 | "reversedAt": null,
54 | "paymentId": "tr_WDqYK6vllg",
55 | "_links": {
56 | "self": {
57 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg/chargebacks/chb_n9z0tp",
58 | "type": "application/hal+json"
59 | },
60 | "payment": {
61 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg",
62 | "type": "application/hal+json"
63 | },
64 | "documentation": {
65 | "href": "https://docs.mollie.com/reference/chargebacks-api/get-chargeback",
66 | "type": "text/html"
67 | }
68 | }
69 | },
70 | { },
71 | { }
72 | ]
73 | },
74 | "_links": {
75 | "self": {
76 | "href": "https://api.mollie.com/v2/payments/tr_7UhSN1zuXS/chargebacks",
77 | "type": "application/hal+json"
78 | },
79 | "documentation": {
80 | "href": "https://docs.mollie.com/reference/chargebacks-api/list-chargebacks",
81 | "type": "text/html"
82 | }
83 | }
84 | }`
85 | )
86 |
--------------------------------------------------------------------------------
/testdata/client_links.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // CreateClientLinkResponse examples.
4 | const CreateClientLinkResponse = `{
5 | "id": "csr_vZCnNQsV2UtfXxYifWKWH",
6 | "resource": "client-link",
7 | "_links": {
8 | "clientLink": {
9 | "href": "https://my.mollie.com/dashboard/client-link/finalize/csr_vZCnNQsV2UtfXxYifWKWH",
10 | "type": "text/html"
11 | },
12 | "documentation": {
13 | "href": "https://docs.mollie.com/reference/clients-api/create-client-link",
14 | "type": "text/html"
15 | }
16 | }
17 | }`
18 |
--------------------------------------------------------------------------------
/testdata/customers.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // GetCustomerResponse example
4 | const GetCustomerResponse = `{
5 | "resource": "customer",
6 | "id": "cst_kEn1PlbGa",
7 | "mode": "test",
8 | "name": "Customer A",
9 | "email": "customer@example.org",
10 | "locale": "nl_NL",
11 | "metadata": null,
12 | "createdAt": "2018-04-06T13:23:21.0Z",
13 | "_links": {
14 | "self": {
15 | "href": "https://api.mollie.com/v2/customers/cst_kEn1PlbGa",
16 | "type": "application/hal+json"
17 | },
18 | "dashboard": {
19 | "href": "https://www.mollie.com/dashboard/org_123456789/customers/cst_kEn1PlbGa",
20 | "type": "text/html"
21 | },
22 | "mandates": {
23 | "href": "https://api.mollie.com/v2/customers/cst_kEn1PlbGa/mandates",
24 | "type": "application/hal+json"
25 | },
26 | "subscriptions": {
27 | "href": "https://api.mollie.com/v2/customers/cst_kEn1PlbGa/subscriptions",
28 | "type": "application/hal+json"
29 | },
30 | "payments": {
31 | "href": "https://api.mollie.com/v2/customers/cst_kEn1PlbGa/payments",
32 | "type": "application/hal+json"
33 | },
34 | "documentation": {
35 | "href": "https://docs.mollie.com/reference/customers-api/get-customer",
36 | "type": "text/html"
37 | }
38 | }
39 | }`
40 |
41 | // CreateCustomerResponse example
42 | const CreateCustomerResponse = `{
43 | "resource": "customer",
44 | "id": "cst_8wmqcHMN4U",
45 | "mode": "test",
46 | "name": "Customer A",
47 | "email": "customer@example.org",
48 | "locale": null,
49 | "metadata": null,
50 | "createdAt": "2018-04-06T13:10:19.0Z",
51 | "_links": {
52 | "self": {
53 | "href": "https://api.mollie.com/v2/customers/cst_8wmqcHMN4U",
54 | "type": "application/hal+json"
55 | },
56 | "documentation": {
57 | "href": "https://docs.mollie.com/reference/customers-api/create-customer",
58 | "type": "text/html"
59 | }
60 | }
61 | }`
62 |
63 | // UpdateCustomerResponse example
64 | const UpdateCustomerResponse = `{
65 | "resource": "customer",
66 | "id": "cst_8wmqcHMN4U",
67 | "mode": "test",
68 | "name": "Updated Customer A",
69 | "email": "updated-customer@example.org",
70 | "locale": "nl_NL",
71 | "metadata": null,
72 | "createdAt": "2018-04-06T13:23:21.0Z",
73 | "_links": {
74 | "self": {
75 | "href": "https://api.mollie.com/v2/customers/cst_8wmqcHMN4U",
76 | "type": "application/hal+json"
77 | },
78 | "documentation": {
79 | "href": "https://docs.mollie.com/reference/customers-api/get-customer",
80 | "type": "text/html"
81 | }
82 | }
83 | }`
84 |
85 | // ListCustomersResponse example
86 | const ListCustomersResponse = `{
87 | "count": 3,
88 | "_embedded": {
89 | "customers": [
90 | {
91 | "resource": "customer",
92 | "id": "cst_kEn1PlbGa",
93 | "mode": "test",
94 | "name": "Customer A",
95 | "email": "customer@example.org",
96 | "locale": "nl_NL",
97 | "metadata": null,
98 | "createdAt": "2018-04-06T13:23:21.0Z",
99 | "_links": {
100 | "self": {
101 | "href": "https://api.mollie.com/v2/customers/cst_kEn1PlbGa",
102 | "type": "application/hal+json"
103 | },
104 | "documentation": {
105 | "href": "https://docs.mollie.com/reference/customers-api/get-customer",
106 | "type": "text/html"
107 | }
108 | }
109 | },
110 | { },
111 | { }
112 | ]
113 | },
114 | "_links": {
115 | "self": {
116 | "href": "https://api.mollie.com/v2/customers",
117 | "type": "application/hal+json"
118 | },
119 | "previous": null,
120 | "next": {
121 | "href": "https://api.mollie.com/v2/customers?from=cst_stTC2WHAuS",
122 | "type": "application/hal+json"
123 | },
124 | "documentation": {
125 | "href": "https://docs.mollie.com/reference/customers-api/list-customers",
126 | "type": "text/html"
127 | }
128 | }
129 | }`
130 |
--------------------------------------------------------------------------------
/testdata/errors.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // UnauthorizedErrorResponse example.
4 | const UnauthorizedErrorResponse = `{
5 | "status": 401,
6 | "title": "Unauthorized Request",
7 | "detail": "Missing authentication, or failed to authenticate",
8 | "_links": {
9 | "documentation": {
10 | "href": "https://docs.mollie.com/overview/authentication",
11 | "type": "text/html"
12 | }
13 | }
14 | }`
15 |
16 | // NotFoundErrorResponse example.
17 | const NotFoundErrorResponse = `{
18 | "status": 404,
19 | "title": "Not Found",
20 | "detail": "No payment exists with token tr_I_dont_exist.",
21 | "_links": {
22 | "documentation": {
23 | "href": "https://docs.mollie.com/errors",
24 | "type": "text/html"
25 | }
26 | }
27 | }`
28 |
29 | // UnprocessableEntityErrorResponse example.
30 | const UnprocessableEntityErrorResponse = `{
31 | "status": 422,
32 | "title": "Unprocessable Entity",
33 | "detail": "The amount is higher than the maximum",
34 | "field": "amount",
35 | "_links": {
36 | "documentation": {
37 | "href": "https://docs.mollie.com/errors",
38 | "type": "text/html"
39 | }
40 | }
41 | }`
42 |
43 | // InternalServerErrorResponse example.
44 | const InternalServerErrorResponse = `{
45 | "status": 500,
46 | "title": "Internal Server Error",
47 | "detail": "An internal server error occurred while processing your request",
48 | "_links": {
49 | "documentation": {
50 | "href": "https://docs.mollie.com/overview/authentication",
51 | "type": "text/html"
52 | }
53 | }
54 | }`
55 |
--------------------------------------------------------------------------------
/testdata/invoices.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // GetInvoiceResponse example
4 | const GetInvoiceResponse = `{
5 | "resource": "invoice",
6 | "id": "inv_xBEbP9rvAq",
7 | "reference": "2016.10000",
8 | "vatNumber": "NL001234567B01",
9 | "status": "open",
10 | "issuedAt": "2016-08-31",
11 | "dueAt": "2016-09-14",
12 | "netAmount": {
13 | "value": "45.00",
14 | "currency": "EUR"
15 | },
16 | "vatAmount": {
17 | "value": "9.45",
18 | "currency": "EUR"
19 | },
20 | "grossAmount": {
21 | "value": "54.45",
22 | "currency": "EUR"
23 | },
24 | "lines":[
25 | {
26 | "period": "2016-09",
27 | "description": "iDEAL transactiekosten",
28 | "count": 100,
29 | "vatPercentage": 21,
30 | "amount": {
31 | "value": "45.00",
32 | "currency": "EUR"
33 | }
34 | }
35 | ],
36 | "_links": {
37 | "self": {
38 | "href": "https://api.mollie.com/v2/invoices/inv_xBEbP9rvAq",
39 | "type": "application/hal+json"
40 | },
41 | "pdf": {
42 | "href": "https://www.mollie.com/merchant/download/invoice/xBEbP9rvAq/2ab44d60b35b1d06090bba955fa2c602",
43 | "type": "application/pdf",
44 | "expiresAt": "2018-11-09T14:10:36+00:00"
45 | }
46 | }
47 | }`
48 |
49 | // ListInvoicesResponse example
50 | const ListInvoicesResponse = `{
51 | "count": 5,
52 | "_embedded": {
53 | "invoices": [
54 | {
55 | "resource": "invoice",
56 | "id": "inv_xBEbP9rvAq",
57 | "reference": "2016.10000",
58 | "vatNumber": "NL001234567B01",
59 | "status": "open",
60 | "issuedAt": "2016-08-31",
61 | "dueAt": "2016-09-14",
62 | "netAmount": {
63 | "value": "45.00",
64 | "currency": "EUR"
65 | },
66 | "vatAmount": {
67 | "value": "9.45",
68 | "currency": "EUR"
69 | },
70 | "grossAmount": {
71 | "value": "54.45",
72 | "currency": "EUR"
73 | },
74 | "lines":[
75 | {
76 | "period": "2016-09",
77 | "description": "iDEAL transactiekosten",
78 | "count": 100,
79 | "vatPercentage": 21,
80 | "amount": {
81 | "value": "45.00",
82 | "currency": "EUR"
83 | }
84 | }
85 | ],
86 | "_links": {
87 | "self": {
88 | "href": "https://api.mollie.com/v2/invoices/inv_xBEbP9rvAq",
89 | "type": "application/hal+json"
90 | },
91 | "pdf": {
92 | "href": "https://www.mollie.com/merchant/download/invoice/xBEbP9rvAq/2ab44d60b35955fa2c602",
93 | "type": "application/pdf",
94 | "expiresAt": "2018-11-09T14:10:36+00:00"
95 | }
96 | }
97 | }
98 | ]
99 | },
100 | "_links": {
101 | "self": {
102 | "href": "https://api.mollie.nl/v2/invoices?limit=5",
103 | "type": "application/hal+json"
104 | },
105 | "previous": null,
106 | "next": {
107 | "href": "https://api.mollie.nl/v2/invoices?from=inv_xBEbP9rvAq&limit=5",
108 | "type": "application/hal+json"
109 | },
110 | "documentation": {
111 | "href": "https://docs.mollie.com/reference/invoices-api/list-invoices",
112 | "type": "text/html"
113 | }
114 | }
115 | }`
116 |
--------------------------------------------------------------------------------
/testdata/mandates.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // CreateMandateResponse
4 | const CreateMandateResponse = `{
5 | "resource": "mandate",
6 | "id": "mdt_h3gAaD5zP",
7 | "mode": "test",
8 | "status": "valid",
9 | "method": "directdebit",
10 | "details": {
11 | "consumerName": "John Doe",
12 | "consumerAccount": "NL55INGB0000000000",
13 | "consumerBic": "INGBNL2A"
14 | },
15 | "mandateReference": "YOUR-COMPANY-MD13804",
16 | "signatureDate": "2018-05-07",
17 | "createdAt": "2018-05-07T10:49:08+00:00",
18 | "_links": {
19 | "self": {
20 | "href": "https://api.mollie.com/v2/customers/cst_4qqhO89gsT/mandates/mdt_h3gAaD5zP",
21 | "type": "application/hal+json"
22 | },
23 | "customer": {
24 | "href": "https://api.mollie.com/v2/customers/cst_4qqhO89gsT",
25 | "type": "application/hal+json"
26 | },
27 | "documentation": {
28 | "href": "https://docs.mollie.com/reference/mandates-api/create-mandate",
29 | "type": "text/html"
30 | }
31 | }
32 | }`
33 |
34 | // GetMandateResponse
35 | const GetMandateResponse = `{
36 | "resource": "mandate",
37 | "id": "mdt_h3gAaD5zP",
38 | "mode": "test",
39 | "status": "valid",
40 | "method": "directdebit",
41 | "details": {
42 | "consumerName": "John Doe",
43 | "consumerAccount": "NL55INGB0000000000",
44 | "consumerBic": "INGBNL2A"
45 | },
46 | "mandateReference": "YOUR-COMPANY-MD1380",
47 | "signatureDate": "2018-05-07",
48 | "createdAt": "2018-05-07T10:49:08+00:00",
49 | "_links": {
50 | "self": {
51 | "href": "https://api.mollie.com/v2/customers/cst_4qqhO89gsT/mandates/mdt_h3gAaD5zP",
52 | "type": "application/hal+json"
53 | },
54 | "customer": {
55 | "href": "https://api.mollie.com/v2/customers/cst_4qqhO89gsT",
56 | "type": "application/hal+json"
57 | },
58 | "documentation": {
59 | "href": "https://docs.mollie.com/reference/mandates-api/get-mandate",
60 | "type": "text/html"
61 | }
62 | }
63 | }`
64 |
65 | const ListMandatesResponse = `
66 | {
67 | "count": 2,
68 | "_embedded": {
69 | "mandates": [
70 | {
71 | "resource": "mandate",
72 | "id": "mdt_AcQl5fdL4h",
73 | "mode": "test",
74 | "status": "valid",
75 | "method": "directdebit",
76 | "details": {
77 | "consumerName": "John Doe",
78 | "consumerAccount": "NL55INGB0000000000",
79 | "consumerBic": "INGBNL2A"
80 | },
81 | "mandateReference": null,
82 | "signatureDate": "2018-05-07",
83 | "createdAt": "2018-05-07T10:49:08+00:00",
84 | "_links": {
85 | "self": {
86 | "href": "https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/mandates/mdt_AcQl5fdL4h",
87 | "type": "application/hal+json"
88 | },
89 | "customer": {
90 | "href": "https://api.mollie.com/v2/customers/cst_8wmqcHMN4U",
91 | "type": "application/hal+json"
92 | },
93 | "documentation": {
94 | "href": "https://mollie.com/en/docs/reference/customers/create-mandate",
95 | "type": "text/html"
96 | }
97 | }
98 | },
99 | {
100 | "resource": "mandate",
101 | "id": "mdt_AcQl5fdL4h",
102 | "mode": "test",
103 | "status": "valid",
104 | "method": "directdebit",
105 | "details": {
106 | "consumerName": "John Doe",
107 | "consumerAccount": "NL55INGB0000000000",
108 | "consumerBic": "INGBNL2A"
109 | },
110 | "mandateReference": null,
111 | "signatureDate": "2018-05-07",
112 | "createdAt": "2018-05-07T10:49:08+00:00",
113 | "_links": {
114 | "self": {
115 | "href": "https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/mandates/mdt_AcQl5fdL4h",
116 | "type": "application/hal+json"
117 | },
118 | "customer": {
119 | "href": "https://api.mollie.com/v2/customers/cst_8wmqcHMN4U",
120 | "type": "application/hal+json"
121 | },
122 | "documentation": {
123 | "href": "https://mollie.com/en/docs/reference/customers/create-mandate",
124 | "type": "text/html"
125 | }
126 | }
127 | }
128 | ]
129 | },
130 | "_links": {
131 | "self": {
132 | "href": "https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/mandates?limit=5",
133 | "type": "application/hal+json"
134 | },
135 | "previous": null,
136 | "next": {
137 | "href": "https://api.mollie.com/v2/customers/cst_8wmqcHMN4U/mandates?from=mdt_AcQl5fdL4h&limit=5",
138 | "type": "application/hal+json"
139 | },
140 | "documentation": {
141 | "href": "https://docs.mollie.com/reference/mandates-api/revoke-mandate",
142 | "type": "text/html"
143 | }
144 | }
145 | }`
146 |
--------------------------------------------------------------------------------
/testdata/miscellaneous.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | const ApplePaySessionResponse = `{
4 | "epochTimestamp": 1555507053169,
5 | "expiresAt": 1555510653169,
6 | "merchantSessionIdentifier": "SSH2EAF8AFAEAA94DEEA898162A5DAFD36E_916523AAED1343F5BC5815E12BEE9250AFFDC1A17C46B0DE5A943F0F94927C24",
7 | "nonce": "0206b8db",
8 | "merchantIdentifier": "BD62FEB196874511C22DB28A9E14A89E3534C93194F73EA417EC566368D391EB",
9 | "domainName": "pay.example.org",
10 | "displayName": "Chuck Norris's Store",
11 | "signature": "308006092a864886f7...8cc030ad3000000000000"
12 | }`
13 |
--------------------------------------------------------------------------------
/testdata/onboarding.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // GetOnboardingStatusResponse example.
4 | const GetOnboardingStatusResponse = `{
5 | "resource": "onboarding",
6 | "name": "Mollie B.V.",
7 | "signedUpAt": "2018-12-20T10:49:08+00:00",
8 | "status": "completed",
9 | "canReceivePayments": true,
10 | "canReceiveSettlements": true,
11 | "_links": {
12 | "self": {
13 | "href": "https://api.mollie.com/v2/onboarding/me",
14 | "type": "application/hal+json"
15 | },
16 | "dashboard": {
17 | "href": "https://www.mollie.com/dashboard/onboarding",
18 | "type": "text/html"
19 | },
20 | "organization": {
21 | "href": "https://api.mollie.com/v2/organization/org_12345",
22 | "type": "application/hal+json"
23 | },
24 | "documentation": {
25 | "href": "https://docs.mollie.com/reference/onboarding-api/get-onboarding-status",
26 | "type": "text/html"
27 | }
28 | }
29 | }
30 | `
31 |
--------------------------------------------------------------------------------
/testdata/organizations.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // GetOrganizationResponse example
4 | const GetOrganizationResponse = `{
5 | "resource": "organization",
6 | "id": "org_12345678",
7 | "name": "Mollie B.V.",
8 | "email": "info@mollie.com",
9 | "address": {
10 | "streetAndNumber": "Keizersgracht 313",
11 | "postalCode": "1016 EE",
12 | "city": "Amsterdam",
13 | "country": "NL"
14 | },
15 | "registrationNumber": "30204462",
16 | "vatNumber": "NL815839091B01",
17 | "_links": {
18 | "self": {
19 | "href": "https://api.mollie.com/v2/organizations/org_12345678",
20 | "type": "application/hal+json"
21 | },
22 | "documentation": {
23 | "href": "https://docs.mollie.com/reference/organizations-api/get-organization",
24 | "type": "text/html"
25 | }
26 | }
27 | }`
28 |
29 | // GetCurrentOrganizationResponse example
30 | const GetCurrentOrganizationResponse = `{
31 | "resource": "organization",
32 | "id": "org_12345678",
33 | "name": "Mollie B.V.",
34 | "email": "info@mollie.com",
35 | "address": {
36 | "streetAndNumber" : "Keizersgracht 313",
37 | "postalCode": "1016 EE",
38 | "city": "Amsterdam",
39 | "country": "NL"
40 | },
41 | "registrationNumber": "30204462",
42 | "vatNumber": "NL815839091B01",
43 | "_links": {
44 | "self": {
45 | "href": "https://api.mollie.com/v2/organizations/me",
46 | "type": "application/hal+json"
47 | },
48 | "chargebacks": {
49 | "href": "https://api.mollie.com/v2/chargebacks",
50 | "type": "application/hal+json"
51 | },
52 | "customers": {
53 | "href": "https://api.mollie.com/v2/customers",
54 | "type": "application/hal+json"
55 | },
56 | "invoices": {
57 | "href": "https://api.mollie.com/v2/invoices",
58 | "type": "application/hal+json"
59 | },
60 | "payments": {
61 | "href": "https://api.mollie.com/v2/payments",
62 | "type": "application/hal+json"
63 | },
64 | "profiles": {
65 | "href": "https://api.mollie.com/v2/profiles",
66 | "type": "application/hal+json"
67 | },
68 | "refunds": {
69 | "href": "https://api.mollie.com/v2/refunds",
70 | "type": "application/hal+json"
71 | },
72 | "settlements": {
73 | "href": "https://api.mollie.com/v2/settlements",
74 | "type": "application/hal+json"
75 | },
76 | "documentation": {
77 | "href": "https://docs.mollie.com/reference/organizations-api/current-organization",
78 | "type": "text/html"
79 | }
80 | }
81 | }`
82 |
83 | const GetPartnerStatusResponse = `{
84 | "resource": "partner",
85 | "partnerType": "signuplink",
86 | "partnerContractSignedAt": "2018-03-20T13:13:37+00:00",
87 | "_links": {
88 | "self": {
89 | "href": "https://api.mollie.com/v2/organizations/me/partner",
90 | "type": "application/hal+json"
91 | },
92 | "documentation": {
93 | "href": "https://docs.mollie.com/reference/partners-api/get-partner",
94 | "type": "text/html"
95 | },
96 | "signuplink": {
97 | "href": "https://www.mollie.com/dashboard/signup/myCode?lang=en",
98 | "type": "text/html"
99 | }
100 | }
101 | }`
102 |
--------------------------------------------------------------------------------
/testdata/partners.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | const ListPartnerClientsResponse = `{
4 | "count":1,
5 | "_embedded":{
6 | "clients":[
7 | {
8 | "resource":"client",
9 | "id":"org_1337",
10 | "organizationCreatedAt":"2018-03-21T13:13:37+00:00",
11 | "commission":{
12 | "count":200,
13 | "totalAmount":{
14 | "currency":"EUR",
15 | "value":"10.00"
16 | }
17 | },
18 | "_links":{
19 | "self":{
20 | "href":"https://api.mollie.com/v2/clients/org_1337",
21 | "type":"application/hal+json"
22 | },
23 | "organization":{
24 | "href":"https://api.mollie.com/v2/organizations/org_1337",
25 | "type":"application/hal+json"
26 | },
27 | "onboarding":{
28 | "href":"https://api.mollie.com/v2/onboarding/org_1337",
29 | "type":"application/hal+json"
30 | },
31 | "documentation":{
32 | "href":"https://docs.mollie.com/reference/partners-api/get-client",
33 | "type":"text/html"
34 | }
35 | }
36 | }
37 | ]
38 | },
39 | "_links":{
40 | "self":{
41 | "href":"https://api.mollie.com/v2/clients?limit=3",
42 | "type":"application/hal+json"
43 | },
44 | "previous":null,
45 | "next":{
46 | "href":"https://api.mollie.com/v2/clients?from=org_1379&limit=3",
47 | "type":"application/hal+json"
48 | },
49 | "documentation":{
50 | "href":"https://docs.mollie.com/reference/partners-api/list-clients",
51 | "type":"text/html"
52 | }
53 | }
54 | }`
55 |
56 | const GetPartnerClientResponse = `{
57 | "resource": "client",
58 | "id": "org_1337",
59 | "organizationCreatedAt": "2018-03-21T13:13:37+00:00",
60 | "commission": {
61 | "count": 200,
62 | "totalAmount": {
63 | "currency": "EUR",
64 | "value": "10.00"
65 | }
66 | },
67 | "_links": {
68 | "self": {
69 | "href": "https://api.mollie.com/v2/clients/org_1337",
70 | "type": "application/hal+json"
71 | },
72 | "organization": {
73 | "href": "https://api.mollie.com/v2/organizations/org_1337",
74 | "type": "application/hal+json"
75 | },
76 | "onboarding": {
77 | "href": "https://api.mollie.com/v2/onboarding/org_1337",
78 | "type": "application/hal+json"
79 | },
80 | "documentation": {
81 | "href": "https://docs.mollie.com/reference/partners-api/get-client",
82 | "type": "text/html"
83 | }
84 | }
85 | }`
86 |
--------------------------------------------------------------------------------
/testdata/payments.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // GetPaymentResponse example
4 | const GetPaymentResponse = `{
5 | "resource": "payment",
6 | "id": "tr_WDqYK6vllg",
7 | "mode": "test",
8 | "createdAt": "2018-03-20T13:13:37+00:00",
9 | "amount": {
10 | "value": "10.00",
11 | "currency": "EUR"
12 | },
13 | "description": "Order #12345",
14 | "method": null,
15 | "metadata": {
16 | "order_id": "12345"
17 | },
18 | "status": "open",
19 | "isCancelable": false,
20 | "locale": "nl_NL",
21 | "restrictPaymentMethodsToCountry": "NL",
22 | "expiresAt": "2018-03-20T13:28:37+00:00",
23 | "details": null,
24 | "profileId": "pfl_QkEhN94Ba",
25 | "sequenceType": "oneoff",
26 | "redirectUrl": "https://webshop.example.org/order/12345/",
27 | "webhookUrl": "https://webshop.example.org/payments/webhook/",
28 | "_links": {
29 | "self": {
30 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg",
31 | "type": "application/hal+json"
32 | },
33 | "checkout": {
34 | "href": "https://www.mollie.com/payscreen/select-method/WDqYK6vllg",
35 | "type": "text/html"
36 | },
37 | "dashboard": {
38 | "href": "https://www.mollie.com/dashboard/org_12345678/payments/tr_WDqYK6vllg",
39 | "type": "application/json"
40 | },
41 | "documentation": {
42 | "href": "https://docs.mollie.com/reference/payments-api/get-payment",
43 | "type": "text/html"
44 | }
45 | }
46 | }`
47 |
48 | // CancelPaymentResponse example
49 | const CancelPaymentResponse = `{
50 | "resource": "payment",
51 | "id": "tr_WDqYK6vllg",
52 | "mode": "live",
53 | "createdAt": "2018-03-19T10:18:33+00:00",
54 | "amount": {
55 | "value": "35.07",
56 | "currency": "EUR"
57 | },
58 | "description": "Order 33",
59 | "method": "banktransfer",
60 | "metadata": null,
61 | "status": "canceled",
62 | "canceledAt": "2018-03-19T10:19:15+00:00",
63 | "details": {
64 | "bankName": "Stichting Mollie Payments",
65 | "bankAccount": "NL53ABNA0627535577",
66 | "bankBic": "ABNANL2A",
67 | "transferReference": "RF12-3456-7890-1234"
68 | },
69 | "profileId": "pfl_QkEhN94Ba",
70 | "sequenceType": "oneoff",
71 | "redirectUrl": "https://webshop.example.org/order/33/",
72 | "_links": {
73 | "self": {
74 | "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg",
75 | "type": "application/hal+json"
76 | },
77 | "documentation": {
78 | "href": "https://docs.mollie.com/reference/payments-api/cancel-payment",
79 | "type": "text/html"
80 | }
81 | }
82 | }`
83 |
84 | // UpdatePaymentResponse example
85 | const UpdatePaymentResponse = `{
86 | "resource": "payment",
87 | "id": "tr_7UhSN1zuXS",
88 | "mode": "test",
89 | "createdAt": "2018-03-20T09:13:37+00:00",
90 | "amount": {
91 | "value": "10.00",
92 | "currency": "EUR"
93 | },
94 | "description": "Order #98765",
95 | "method": null,
96 | "metadata": {
97 | "order_id": "98765"
98 | },
99 | "status": "open",
100 | "isCancelable": false,
101 | "expiresAt": "2018-03-20T09:28:37+00:00",
102 | "details": null,
103 | "profileId": "pfl_QkEhN94Ba",
104 | "sequenceType": "oneoff",
105 | "redirectUrl": "https://example.org/webshop/order/98765/",
106 | "webhookUrl": "https://example.org/webshop/payments/webhook/",
107 | "_links": {
108 | "self": {
109 | "href": "https://api.mollie.com/v2/payments/tr_7UhSN1zuXS",
110 | "type": "application/json"
111 | },
112 | "checkout": {
113 | "href": "https://www.mollie.com/payscreen/select-method/7UhSN1zuXS",
114 | "type": "text/html"
115 | },
116 | "documentation": {
117 | "href": "https://docs.mollie.com/reference/payments-api/update-payment",
118 | "type": "text/html"
119 | }
120 | }
121 | }
122 | `
123 |
124 | // ListPaymentsResponse example
125 | const ListPaymentsResponse = `{
126 | "count": 5,
127 | "_embedded": {
128 | "payments": [
129 | {
130 | "resource": "payment",
131 | "id": "tr_7UhSN1zuXS",
132 | "mode": "test",
133 | "createdAt": "2018-02-12T11:58:35.0Z",
134 | "expiresAt": "2018-02-12T12:13:35.0Z",
135 | "status": "open",
136 | "isCancelable": false,
137 | "amount": {
138 | "value": "75.00",
139 | "currency": "GBP"
140 | },
141 | "description": "Order #12345",
142 | "method": "ideal",
143 | "metadata": null,
144 | "details": null,
145 | "profileId": "pfl_QkEhN94Ba",
146 | "redirectUrl": "https://webshop.example.org/order/12345/",
147 | "_links": {
148 | "checkout": {
149 | "href": "https://www.mollie.com/paymentscreen/issuer/select/ideal/7UhSN1zuXS",
150 | "type": "text/html"
151 | },
152 | "self": {
153 | "href": "https://api.mollie.com/v2/payments/tr_7UhSN1zuXS",
154 | "type": "application/hal+json"
155 | }
156 | }
157 | }
158 | ]
159 | },
160 | "_links": {
161 | "self": {
162 | "href": "https://api.mollie.com/v2/payments?limit=5",
163 | "type": "application/hal+json"
164 | },
165 | "previous": null,
166 | "next": {
167 | "href": "https://api.mollie.com/v2/payments?from=tr_SDkzMggpvx&limit=5",
168 | "type": "application/hal+json"
169 | },
170 | "documentation": {
171 | "href": "https://docs.mollie.com/reference/payments-api/list-payments",
172 | "type": "text/html"
173 | }
174 | }
175 | }`
176 |
--------------------------------------------------------------------------------
/testdata/permissions.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // GetPermissionsResponse example.
4 | const GetPermissionsResponse = `{
5 | "resource": "permission",
6 | "id": "payments.read",
7 | "description": "View your payments",
8 | "granted": true,
9 | "_links": {
10 | "self": {
11 | "href": "https://api.mollie.com/v2/permissions/payments.read",
12 | "type": "application/hal+json"
13 | },
14 | "documentation": {
15 | "href": "https://docs.mollie.com/reference/permissions-api/get-permission",
16 | "type": "text/html"
17 | }
18 | }
19 | }
20 | `
21 |
22 | // ListPermissionsResponse example.
23 | const ListPermissionsResponse = `{
24 | "_embedded": {
25 | "permissions": [
26 | {
27 | "resource": "permission",
28 | "id": "payments.write",
29 | "description": "Create new payments",
30 | "granted": false,
31 | "_links": {
32 | "self": {
33 | "href": "https://api.mollie.com/v2/permissions/payments.write",
34 | "type": "application/hal+json"
35 | }
36 | }
37 | },
38 | {
39 | "resource": "permission",
40 | "id": "payments.read",
41 | "description": "View your payments",
42 | "granted": true,
43 | "_links": {
44 | "self": {
45 | "href": "https://api.mollie.com/v2/permissions/payments.read",
46 | "type": "application/hal+json"
47 | }
48 | }
49 | }
50 | ]
51 | },
52 | "count": 15,
53 | "_links": {
54 | "documentation": {
55 | "href": "https://docs.mollie.com/reference/permissions-api/list-permissions",
56 | "type": "text/html"
57 | },
58 | "self": {
59 | "href": "https://api.mollie.com/v2/permissions",
60 | "type": "application/hal+json"
61 | }
62 | }
63 | }
64 | `
65 |
--------------------------------------------------------------------------------
/testdata/subscriptions.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // GetSubscriptionResponse example
4 | const GetSubscriptionResponse = `{
5 | "resource": "subscription",
6 | "id": "sub_rVKGtNd6s3",
7 | "mode": "live",
8 | "createdAt": "2016-06-01T12:23:34+00:00",
9 | "status": "active",
10 | "amount": {
11 | "value": "25.00",
12 | "currency": "EUR"
13 | },
14 | "times": 4,
15 | "timesRemaining": 4,
16 | "interval": "3 months",
17 | "startDate": "2016-06-01",
18 | "nextPaymentDate": "2016-09-01",
19 | "description": "Quarterly payment",
20 | "method": null,
21 | "mandateId": "mdt_38HS4fsS",
22 | "webhookUrl": "https://webshop.example.org/payments/webhook",
23 | "metadata": {
24 | "plan": "small"
25 | },
26 | "_links": {
27 | "self": {
28 | "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS/subscriptions/sub_rVKGtNd6s3",
29 | "type": "application/hal+json"
30 | },
31 | "customer": {
32 | "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS",
33 | "type": "application/hal+json"
34 | },
35 | "profile": {
36 | "href": "https://api.mollie.com/v2/profiles/pfl_URR55HPMGx",
37 | "type": "application/hal+json"
38 | },
39 | "payments": {
40 | "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS/subscriptions/sub_rVKGtNd6s3/payments",
41 | "type": "application/hal+json"
42 | },
43 | "documentation": {
44 | "href": "https://docs.mollie.com/reference/subscriptions-api/get-subscription",
45 | "type": "text/html"
46 | }
47 | }
48 | }`
49 |
50 | // DeleteSubscriptionResponse example
51 | const DeleteSubscriptionResponse = `{
52 | "resource": "subscription",
53 | "id": "sub_rVKGtNd6s3",
54 | "mode": "live",
55 | "createdAt": "2018-06-01T12:23:34+00:00",
56 | "status": "canceled",
57 | "amount": {
58 | "value": "25.00",
59 | "currency": "EUR"
60 | },
61 | "times": 4,
62 | "interval": "3 months",
63 | "nextPaymentDate": null,
64 | "description": "Quarterly payment",
65 | "method": null,
66 | "startDate": "2016-06-01",
67 | "webhookUrl": "https://webshop.example.org/payments/webhook",
68 | "canceledAt": "2018-08-01T11:04:21+00:00",
69 | "_links": {
70 | "self": {
71 | "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS/subscriptions/sub_rVKGtNd6s3",
72 | "type": "application/hal+json"
73 | },
74 | "customer": {
75 | "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS",
76 | "type": "application/hal+json"
77 | },
78 | "documentation": {
79 | "href": "https://docs.mollie.com/reference/subscriptions-api/cancel-subscription",
80 | "type": "text/html"
81 | }
82 | }
83 | }`
84 |
85 | // ListAllSubscriptionsResponse example
86 | const ListAllSubscriptionsResponse = `{
87 | "count": 3,
88 | "_embedded": {
89 | "subscriptions": [
90 | {
91 | "resource": "subscription",
92 | "id": "sub_rVKGtNd6s3",
93 | "mode": "live",
94 | "createdAt": "2018-06-01T12:23:34+00:00",
95 | "status": "active",
96 | "amount": {
97 | "value": "25.00",
98 | "currency": "EUR"
99 | },
100 | "times": 4,
101 | "timesRemaining": 3,
102 | "interval": "3 months",
103 | "startDate": "2016-06-01",
104 | "nextPaymentDate": "2016-09-01",
105 | "description": "Quarterly payment",
106 | "method": null,
107 | "webhookUrl": "https://webshop.example.org/subscriptions/webhook",
108 | "_links": {
109 | "self": {
110 | "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS/subscriptions/sub_rVKGtNd6s3",
111 | "type": "application/hal+json"
112 | },
113 | "profile": {
114 | "href": "https://api.mollie.com/v2/profiles/pfl_URR55HPMGx",
115 | "type": "application/hal+json"
116 | },
117 | "customer": {
118 | "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS",
119 | "type": "application/hal+json"
120 | }
121 | }
122 | }
123 | ]
124 | },
125 | "_links": {
126 | "self": {
127 | "href": "https://api.mollie.com/v2/subscriptions",
128 | "type": "application/hal+json"
129 | },
130 | "previous": null,
131 | "next": {
132 | "href": "https://api.mollie.com/v2/subscriptions?from=sub_mnfbwhMfvo",
133 | "type": "application/hal+json"
134 | },
135 | "documentation": {
136 | "href": "https://docs.mollie.com/reference/subscriptions-api/list-all-subscriptions",
137 | "type": "text/html"
138 | }
139 | }
140 | }`
141 |
--------------------------------------------------------------------------------
/testdata/terminals.go:
--------------------------------------------------------------------------------
1 | package testdata
2 |
3 | // ListTerminalsResponse example.
4 | const ListTerminalsResponse = `{
5 | "count": 5,
6 | "_embedded": {
7 | "terminals": [
8 | {
9 | "id": "term_7MgL4wea46qkRcoTZjWEH",
10 | "profileId": "pfl_QkEhN94Ba",
11 | "status": "active",
12 | "brand": "PAX",
13 | "model": "A920",
14 | "serialNumber": "1234567890",
15 | "currency": "EUR",
16 | "description": "Terminal #12341",
17 | "createdAt": "2022-02-12T11:58:35.0Z",
18 | "updatedAt": "2022-11-15T13:32:11.0Z",
19 | "_links": {
20 | "self": {
21 | "href": "https://api.mollie.com/v2/terminals/term_7MgL4wea46qkRcoTZjWEH",
22 | "type": "application/hal+json"
23 | }
24 | }
25 | },
26 | {
27 | "id": "term_7sgL4wea46qkRcoysdiWEH",
28 | "profileId": "pfl_QkEhN94Ba",
29 | "status": "active",
30 | "brand": "PAX",
31 | "model": "A920",
32 | "serialNumber": "1234567890",
33 | "currency": "MEX",
34 | "description": "Terminal #12342",
35 | "createdAt": "2022-02-12T11:58:35.0Z",
36 | "updatedAt": "2022-11-15T13:32:11.0Z",
37 | "_links": {
38 | "self": {
39 | "href": "https://api.mollie.com/v2/terminals/term_7sgL4wea46qkRcoysdiWEH",
40 | "type": "application/hal+json"
41 | }
42 | }
43 | },
44 | {
45 | "id": "term_7MgLsdD*b3asDayWEH",
46 | "profileId": "pfl_QkEhN94Ba",
47 | "status": "active",
48 | "brand": "PAX",
49 | "model": "A920",
50 | "serialNumber": "1234567890",
51 | "currency": "GBP",
52 | "description": "Terminal #12343",
53 | "createdAt": "2022-02-12T11:58:35.0Z",
54 | "updatedAt": "2022-11-15T13:32:11.0Z",
55 | "_links": {
56 | "self": {
57 | "href": "https://api.mollie.com/v2/terminals/term_7MgLsdD*b3asDayWEH",
58 | "type": "application/hal+json"
59 | }
60 | }
61 | },
62 | {
63 | "id": "term_7MgL4j5jAowWqkRcoTZjWEH",
64 | "profileId": "pfl_QkEhN94Ba",
65 | "status": "active",
66 | "brand": "PAX",
67 | "model": "A920",
68 | "serialNumber": "1234567890",
69 | "currency": "DLS",
70 | "description": "Terminal #12344",
71 | "createdAt": "2022-02-12T11:58:35.0Z",
72 | "updatedAt": "2022-11-15T13:32:11.0Z",
73 | "_links": {
74 | "self": {
75 | "href": "https://api.mollie.com/v2/terminals/term_7MgL4j5jAowWqkRcoTZjWEH",
76 | "type": "application/hal+json"
77 | }
78 | }
79 | },
80 | {
81 | "id": "term_7MgL4we02ujSeRcoTZjWEH",
82 | "profileId": "pfl_QkEhN94Ba",
83 | "status": "active",
84 | "brand": "PAX",
85 | "model": "A920",
86 | "serialNumber": "1234567890",
87 | "currency": "COP",
88 | "description": "Terminal #12345",
89 | "createdAt": "2022-02-12T11:58:35.0Z",
90 | "updatedAt": "2022-11-15T13:32:11.0Z",
91 | "_links": {
92 | "self": {
93 | "href": "https://api.mollie.com/v2/terminals/term_7MgL4we02ujSeRcoTZjWEH",
94 | "type": "application/hal+json"
95 | }
96 | }
97 | }
98 | ]
99 | },
100 | "_links": {
101 | "self": {
102 | "href": "https://api.mollie.com/v2/terminals?limit=5",
103 | "type": "application/hal+json"
104 | },
105 | "previous": null,
106 | "next": {
107 | "href": "https://api.mollie.com/v2/terminals?from=term_7MgL4we02ujSeRcoTZjWEH&limit=5",
108 | "type": "application/hal+json"
109 | },
110 | "documentation": {
111 | "href": "https://docs.mollie.com/reference/terminals-api/list-terminals",
112 | "type": "text/html"
113 | }
114 | }
115 | }`
116 |
117 | // GetTerminalResponse example.
118 | const GetTerminalResponse = `{
119 | "id": "term_7MgL4wea46qkRcoTZjWEH",
120 | "profileId": "pfl_QkEhN94Ba",
121 | "status": "active",
122 | "brand": "PAX",
123 | "model": "A920",
124 | "serialNumber": "1234567890",
125 | "currency": "EUR",
126 | "description": "Terminal #12345",
127 | "createdAt": "2022-02-12T11:58:35.0Z",
128 | "updatedAt": "2022-11-15T13:32:11.0Z",
129 | "_links": {
130 | "self": {
131 | "href": "https://api.mollie.com/v2/terminals/term_7MgL4wea46qkRcoTZjWEH",
132 | "type": "application/hal+json"
133 | },
134 | "documentation": {
135 | "href": "https://docs.mollie.com/reference/terminals-api/get-terminal",
136 | "type": "text/html"
137 | }
138 | }
139 | }`
140 |
--------------------------------------------------------------------------------