├── .gitignore ├── LICENSE ├── README.md ├── charges.go ├── client.go ├── credentials.go ├── events.go ├── example └── example.go ├── go.mod ├── go.sum ├── models.go ├── tests ├── charges_test.go └── events_test.go └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .envrc 3 | response.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2023-present Coinbase Global, Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Commerce Go SDK README 3 | 4 | [![GoDoc](https://godoc.org/github.com/coinbase-samples/commerce-sdk-go?status.svg)](https://godoc.org/github.com/coinbase-samples/commerce-sdk-go) 5 | [![Go Report Card](https://goreportcard.com/badge/coinbase-samples/commerce-sdk-go)](https://goreportcard.com/report/coinbase-samples/commerce-sdk-go) 6 | 7 | ## Overview 8 | 9 | The *Commerce Go SDK* is a sample library that demonstrates the structure of a [Coinbase Commerce](https://commerce.coinbase.com/) driver for the [REST APIs](https://docs.cloud.coinbase.com/commerce/reference). 10 | 11 | ## License 12 | 13 | The _Commerce Go SDK_ sample library is free and open source and released under the [Apache License, Version 2.0](https://github.com/coinbase-samples/commerce-sdk-go/blob/main/LICENSE). 14 | 15 | The application and code are only available for demonstration purposes. 16 | 17 | ## Usage 18 | 19 | To use the _Commerce Go SDK_, initialize the Credentials struct and create a new client. The Credentials struct is JSON enabled. Ensure that Commerce API credentials are stored in a secure manner. 20 | 21 | ### Example 22 | 23 | This code snippet reads the value of the environment variable `COMMERCE_API_KEY` to initiate a new Commerce client. 24 | 25 | **Copy Commerce SDK repository** 26 | 27 | ``` 28 | git clone git@github.com:coinbase-samples/commerce-sdk-go.git 29 | ``` 30 | 31 | **Set the API key as an environment variables in a terminal application** 32 | 33 | ```bash 34 | export COMMERCE_API_KEY= 35 | ``` 36 | 37 | **Initialize the Commerce client with credentials** 38 | 39 | ```go 40 | creds, err := commerce.ReadEnvCredentials("COMMERCE_API_KEY") 41 | if err != nil { 42 | fmt.Printf("Error reading environmental variable: %s", err) 43 | } 44 | 45 | client := commerce.NewClient(creds, http.Client{},) 46 | ``` 47 | 48 | Once a client is initialized, you may call any of the functions. For example, to create a charge, 49 | 50 | ```go 51 | func main() { 52 | // Initialize credentials struct 53 | creds, err := commerce.ReadEnvCredentials("COMMERCE_API_KEY") 54 | if err != nil { 55 | fmt.Printf("Error reading environmental variable: %s", err) 56 | } 57 | 58 | // Initialize client 59 | client := commerce.NewClient(creds, http.Client{}) 60 | 61 | // Add desired context 62 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 63 | defer cancel() 64 | 65 | charge, err := client.CreateCharge(ctx, &commerce.ChargeRequest{ 66 | PricingType: "fixed_price", 67 | LocalPrice: &commerce.LocalPrice{ 68 | Amount: "1.00", 69 | Currency: "USD", 70 | }, 71 | }) 72 | 73 | if err != nil { 74 | fmt.Printf("error: %s\n", err) 75 | } 76 | 77 | // Print the hosted url 78 | fmt.Printf("hosted url: %s\n", charge.Data.HostedURL) 79 | } 80 | ``` 81 | 82 | ### Quickstart example 83 | 84 | The ["example"](https://github.com/coinbase-samples/commerce-sdk-go/tree/main/example) folder will contain the logic to create a charge in the amount of $1.00. To see this in action: 85 | 86 | 1. Change into the 'example' directory by running: `cd example` 87 | 2. Run `go run example.go` 88 | 89 | Expected output: 90 | 91 | ``` 92 | created a charge ID: 64e05b38-a938-4620-a9ca-e3b806b3757b 93 | got a hosted url: https://commerce.coinbase.com/charges/EGTHQJXZ 94 | ``` 95 | 96 | ### Obtaining Coinbase Commerce credentials 97 | 98 | Coinbase Commerce API keys can be created in the Commerce UI under [Settings --> Security.](https://beta.commerce.coinbase.com/settings/security) 99 | -------------------------------------------------------------------------------- /charges.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023-present Coinbase Global, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package commerce 17 | 18 | import ( 19 | "bytes" 20 | "context" 21 | "encoding/json" 22 | "errors" 23 | "fmt" 24 | "io" 25 | "net/http" 26 | ) 27 | 28 | const ( 29 | chargesEndpoint = "/charges" 30 | ) 31 | 32 | func (c Client) CreateCharge(ctx context.Context, req *ChargeRequest) (*ChargeResponse, error) { 33 | 34 | if req.LocalPrice == nil { 35 | return nil, errors.New("LocalPrice is required for ChargeRequest") 36 | } 37 | 38 | if req.PricingType == "" { 39 | return nil, errors.New("PricingType is required for ChargeRequest") 40 | } 41 | 42 | url := fmt.Sprintf("%s%s", c.HttpBaseUrl, chargesEndpoint) 43 | 44 | payload, err := json.Marshal(req) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(payload)) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | c.setHeaders(httpReq) 55 | 56 | resp, err := c.HttpClient.Do(httpReq) 57 | 58 | if err != nil { 59 | return nil, err 60 | } 61 | defer resp.Body.Close() 62 | 63 | if err := handleErrorResponse(resp); err != nil { 64 | return nil, err 65 | } 66 | 67 | body, err := io.ReadAll(resp.Body) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | chargeResponse := &ChargeResponse{} 73 | if err = json.Unmarshal(body, chargeResponse); err != nil { 74 | return nil, err 75 | } 76 | 77 | return chargeResponse, nil 78 | } 79 | 80 | func (c Client) GetCharge(ctx context.Context, chargeId string) (*ChargeResponse, error) { 81 | 82 | url := fmt.Sprintf("%s%s/%s", c.HttpBaseUrl, chargesEndpoint, chargeId) 83 | 84 | httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | c.setHeaders(httpReq) 90 | 91 | resp, err := c.HttpClient.Do(httpReq) 92 | if err != nil { 93 | return nil, err 94 | } 95 | 96 | defer resp.Body.Close() 97 | 98 | if err := handleErrorResponse(resp); err != nil { 99 | return nil, err 100 | } 101 | 102 | body, err := io.ReadAll(resp.Body) 103 | if err != nil { 104 | return nil, err 105 | } 106 | 107 | chargeResponse := &ChargeResponse{} 108 | if err := json.Unmarshal(body, chargeResponse); err != nil { 109 | return nil, err 110 | } 111 | 112 | return chargeResponse, nil 113 | } 114 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023-present Coinbase Global, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package commerce 18 | 19 | import "net/http" 20 | 21 | var baseUrl = "https://api.commerce.coinbase.com" 22 | 23 | type Client struct { 24 | HttpClient http.Client 25 | Credentials *Credentials 26 | HttpBaseUrl string 27 | } 28 | 29 | func (c *Client) BaseUrl(u string) *Client { 30 | c.HttpBaseUrl = u 31 | return c 32 | } 33 | 34 | func (c *Client) setHeaders(req *http.Request) { 35 | req.Header.Set("X-CC-Api-Key", c.Credentials.ApiKey) 36 | req.Header.Set("X-CC-Version", "2018-03-22") 37 | req.Header.Set("Content-Type", "application/json") 38 | req.Header.Set("Accept", "application/json") 39 | } 40 | 41 | func NewClient(credentials *Credentials, httpClient http.Client) *Client { 42 | return &Client{ 43 | HttpClient: httpClient, 44 | Credentials: credentials, 45 | HttpBaseUrl: baseUrl, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /credentials.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023-present Coinbase Global, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package commerce 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | ) 23 | 24 | func ReadEnvCredentials(variableName string) (*Credentials, error) { 25 | apiKey := os.Getenv(variableName) 26 | if apiKey == "" { 27 | return nil, fmt.Errorf("%s not set as an environment variable", variableName) 28 | } 29 | 30 | return &Credentials{ApiKey: apiKey}, nil 31 | } 32 | -------------------------------------------------------------------------------- /events.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023-present Coinbase Global, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package commerce 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "errors" 23 | "fmt" 24 | "io" 25 | "net/http" 26 | ) 27 | 28 | func (c *Client) ListEvents(ctx context.Context) (*EventResponse, error) { 29 | url := c.HttpBaseUrl + "/events" 30 | 31 | httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | c.setHeaders(httpReq) 37 | 38 | resp, err := c.HttpClient.Do(httpReq) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | defer resp.Body.Close() 44 | 45 | if err := handleErrorResponse(resp); err != nil { 46 | return nil, err 47 | } 48 | 49 | body, err := io.ReadAll(resp.Body) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | eventResponse := &EventResponse{} 55 | if err := json.Unmarshal(body, eventResponse); err != nil { 56 | return nil, err 57 | } 58 | 59 | return eventResponse, nil 60 | } 61 | 62 | func (c *Client) ShowEvent(ctx context.Context, eventId string) (*SingleEvent, error) { 63 | 64 | if eventId == "" { 65 | return nil, errors.New("Please enter an eventId") 66 | } 67 | 68 | url := fmt.Sprintf("%s/events/%s", c.HttpBaseUrl, eventId) 69 | 70 | httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | c.setHeaders(httpReq) 76 | 77 | resp, err := c.HttpClient.Do(httpReq) 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | defer resp.Body.Close() 83 | 84 | if err := handleErrorResponse(resp); err != nil { 85 | return nil, err 86 | } 87 | 88 | body, err := io.ReadAll(resp.Body) 89 | if err != nil { 90 | return nil, err 91 | } 92 | 93 | eventData := &SingleEvent{} 94 | if err = json.Unmarshal(body, eventData); err != nil { 95 | return nil, err 96 | } 97 | 98 | return eventData, nil 99 | } 100 | -------------------------------------------------------------------------------- /example/example.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023-present Coinbase Global, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package main 17 | 18 | import ( 19 | "context" 20 | "fmt" 21 | "log" 22 | "net/http" 23 | "time" 24 | 25 | "github.com/coinbase-samples/commerce-sdk-go" 26 | ) 27 | 28 | func main() { 29 | creds, err := commerce.ReadEnvCredentials("COMMERCE_API_KEY") 30 | if err != nil { 31 | fmt.Printf("Error reading environmental variable: %s", err) 32 | } 33 | 34 | client := commerce.NewClient(creds, http.Client{}) 35 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 36 | defer cancel() 37 | 38 | charge, err := client.CreateCharge(ctx, &commerce.ChargeRequest{ 39 | PricingType: "fixed_price", 40 | LocalPrice: &commerce.LocalPrice{ 41 | Amount: "1.00", 42 | Currency: "USD", 43 | }, 44 | }) 45 | 46 | if err != nil { 47 | if comErr, ok := err.(*commerce.CommerceError); ok { 48 | log.Fatalf("api error creating charge: %v", comErr) 49 | } else { 50 | log.Fatalf("error: %s\n", err) 51 | } 52 | } 53 | 54 | fmt.Printf("charge created \n Id: %v\n hosted_url: %s", charge.Data.Id, charge.Data.HostedUrl) 55 | } 56 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/coinbase-samples/commerce-sdk-go 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinbase-samples/commerce-sdk-go/7e33d62fcf500f0f079dc9b6bd374b532ae51f09/go.sum -------------------------------------------------------------------------------- /models.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023-present Coinbase Global, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package commerce 18 | 19 | import ( 20 | "fmt" 21 | "time" 22 | ) 23 | 24 | type Credentials struct { 25 | ApiKey string `json:"X-CC-Api-Key"` 26 | } 27 | 28 | type ChargeRequest struct { 29 | BuyerLocale string `json:"Buyer_locale,omitempty"` 30 | PricingType string `json:"pricing_type"` 31 | LocalPrice *LocalPrice `json:"local_price"` 32 | Metadata *map[string]interface{} `json:"metadata,omitempty"` 33 | RedirectUrl string `json:"redirect_url,omitempty"` 34 | CancelUrl string `json:"cancel_url,omitempty"` 35 | } 36 | 37 | type LocalPrice struct { 38 | Amount string `json:"amount"` 39 | Currency string `json:"currency"` 40 | } 41 | 42 | type ChargeResponse struct { 43 | Data ChargeData `json:"data"` 44 | } 45 | 46 | type ChargeData struct { 47 | BrandColor string `json:"brand_color"` 48 | BrandLogoUrl string `json:"brand_logo_url"` 49 | ChargeKind string `json:"charge_kind"` 50 | Code string `json:"code"` 51 | ConfirmedAt string `json:"confirmed_at"` 52 | CreatedAt string `json:"created_at"` 53 | ExpiresAt string `json:"expires_at"` 54 | HostedUrl string `json:"hosted_url"` 55 | Id string `json:"id"` 56 | OrganizationName string `json:"organization_name"` 57 | Pricing Pricing `json:"pricing"` 58 | PricingType string `json:"pricing_type"` 59 | Redirects Redirects `json:"redirects"` 60 | SupportEmail string `json:"support_email"` 61 | Timeline []Timeline `json:"timeline"` 62 | Web3Data Web3Data `json:"web3_data"` 63 | } 64 | 65 | type Pricing struct { 66 | Local Price `json:"local"` 67 | Settlement Price `json:"settlement"` 68 | } 69 | 70 | type Price struct { 71 | Amount string `json:"amount"` 72 | Currency string `json:"currency"` 73 | } 74 | 75 | type Redirects struct { 76 | CancelUrl string `json:"cancel_url"` 77 | SuccessUrl string `json:"success_url"` 78 | WillRedirectAfterSuccess bool `json:"will_redirect_after_success"` 79 | } 80 | 81 | type Timeline struct { 82 | Status string `json:"status"` 83 | Time string `json:"time"` 84 | } 85 | 86 | type Web3Data struct { 87 | TransferIntent TransferIntent `json:"transfer_intent"` 88 | SuccessEvents []Event `json:"success_events"` 89 | FailureEvents []interface{} `json:"failure_events"` 90 | ContractAddresses map[string]string `json:"contract_addresses"` 91 | } 92 | 93 | type TransferIntent struct { 94 | CallData CallData `json:"call_data"` 95 | ResponseMetadata ResponseMetadata `json:"metadata"` 96 | } 97 | 98 | type CallData struct { 99 | Deadline string `json:"deadline"` 100 | FeeAmount string `json:"fee_amount"` 101 | Id string `json:"id"` 102 | Operator string `json:"operator"` 103 | Prefix string `json:"prefix"` 104 | Recipient string `json:"recipient"` 105 | RecipientAmount string `json:"recipient_amount"` 106 | RecipientCurrency string `json:"recipient_currency"` 107 | RefundDestination string `json:"refund_destination"` 108 | Signature string `json:"signature"` 109 | } 110 | 111 | type ResponseMetadata struct { 112 | ChainId int `json:"chain_id"` 113 | ContractAddress string `json:"contract_address"` 114 | Sender string `json:"sender"` 115 | } 116 | 117 | type Event struct { 118 | Finalized bool `json:"finalized"` 119 | InputTokenAddress string `json:"input_token_address"` 120 | InputTokenAmount string `json:"input_token_amount"` 121 | NetworkFeePaid string `json:"network_fee_paid"` 122 | Recipient string `json:"recipient"` 123 | Sender string `json:"sender"` 124 | Timestamp string `json:"timestamp"` 125 | TxHsh string `json:"tx_hsh"` 126 | } 127 | 128 | type ErrorMessage struct { 129 | Value string `json:"message"` 130 | } 131 | 132 | type EventResponse struct { 133 | Pagination Pagination `json:"pagination"` 134 | Data []EventData `json:"data"` 135 | } 136 | 137 | type Pagination struct { 138 | Order string `json:"order"` 139 | StartingAfter *string `json:"starting_after"` 140 | EndingBefore *string `json:"ending_before"` 141 | Total int32 `json:"total"` 142 | Limit int32 `json:"limit"` 143 | Yielded int32 `json:"yielded"` 144 | PreviousUri string `json:"previous_uri"` 145 | NextUri string `json:"next_uri"` 146 | Data ChargeData `json:"data"` 147 | CursorRange []string `json:"cursor_range"` 148 | } 149 | 150 | type SingleEvent struct { 151 | Data EventData `json:"data"` 152 | Warnings *map[string]interface{} `json:"warnings"` 153 | } 154 | 155 | type EventData struct { 156 | ApiVersion string `json:"api_version"` 157 | CreatedAt time.Time `json:"created_at"` 158 | Data DetailedData `json:"data"` 159 | ID string `json:"id"` 160 | Resource string `json:"resource"` 161 | Type string `json:"type"` 162 | } 163 | 164 | type DetailedData struct { 165 | Id string `json:"id"` 166 | Code string `json:"code"` 167 | Pricing Pricing `json:"pricing"` 168 | Metadata *map[string]interface{} `json:"metadata,omitempty"` 169 | Timeline []Timeline `json:"timeline"` 170 | Redirects Redirects `json:"redirects"` 171 | Web3Data Web3Data `json:"web3_data"` 172 | CreatedAt time.Time `json:"created_at"` 173 | ExpiresAt time.Time `json:"expires_at"` 174 | HostedUrl string `json:"hosted_url"` 175 | BrandColor string `json:"brand_color"` 176 | ChargeKind string `json:"charge_kind"` 177 | PricingType string `json:"pricing_type"` 178 | SupportEmail string `json:"support_email"` 179 | BrandLogoUrl string `json:"brand_logo_url"` 180 | OrgName string `json:"organization_name"` 181 | } 182 | 183 | type ApiErrorDetail struct { 184 | Type string `json:"type"` 185 | Message string `json:"message"` 186 | } 187 | 188 | type ChargeError struct { 189 | Status int `json:"status"` 190 | Error ApiErrorDetail `json:"error"` 191 | Warnings []string `json:"warnings"` 192 | } 193 | 194 | type CommerceError struct { 195 | ApiError *ChargeError 196 | Err error 197 | } 198 | 199 | func (e CommerceError) Error() string { 200 | if e.ApiError != nil { 201 | return fmt.Sprintf("Commerce API error: %v, warnings: %v", e.ApiError.Error, e.ApiError.Warnings) 202 | } 203 | return e.Err.Error() 204 | } 205 | -------------------------------------------------------------------------------- /tests/charges_test.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023-present Coinbase Global, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | "net/http" 24 | "testing" 25 | "time" 26 | 27 | "github.com/coinbase-samples/commerce-sdk-go" 28 | ) 29 | 30 | func TestCreateCharge(t *testing.T) { 31 | pricing_type := "fixed_price" 32 | currency := "USD" 33 | chargeAmount := "1.00" 34 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 35 | defer cancel() 36 | 37 | creds, err := commerce.ReadEnvCredentials("COMMERCE_API_KEY") 38 | if err != nil { 39 | t.Fatalf("error retireving credentials: %s ", err) 40 | } 41 | 42 | c := commerce.NewClient(creds, http.Client{}) 43 | req := &commerce.ChargeRequest{ 44 | PricingType: pricing_type, 45 | LocalPrice: &commerce.LocalPrice{ 46 | Amount: chargeAmount, 47 | Currency: currency, 48 | }, 49 | } 50 | 51 | chargeResponse, err := c.CreateCharge(ctx, req) 52 | if err != nil { 53 | if commErr, ok := err.(commerce.CommerceError); ok { 54 | t.Fatalf("system error creating charge : %v", commErr) 55 | } else { 56 | t.Fatalf("error creating charge: %s", err) 57 | } 58 | } 59 | 60 | formattedResponse, err := json.MarshalIndent(chargeResponse, " ", " ") 61 | if err != nil { 62 | t.Fatalf("error formatting charge: %b", err) 63 | } 64 | 65 | fmt.Print(string(formattedResponse)) 66 | } 67 | -------------------------------------------------------------------------------- /tests/events_test.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023-present Coinbase Global, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | "net/http" 24 | "reflect" 25 | "testing" 26 | "time" 27 | 28 | "github.com/coinbase-samples/commerce-sdk-go" 29 | ) 30 | 31 | func TestShowEvent(t *testing.T) { 32 | 33 | eventId := "2c63ac0e-24a5-4a63-a28a-affbc92ade75" 34 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 35 | defer cancel() 36 | 37 | creds, err := commerce.ReadEnvCredentials("COMMERCE_API_KEY") 38 | if err != nil { 39 | t.Fatalf("error retireving credentials: %s ", err) 40 | } 41 | 42 | c := commerce.NewClient(creds, http.Client{}) 43 | 44 | eventData, err := c.ShowEvent(ctx, eventId) 45 | if reflect.ValueOf(eventData).IsZero() { 46 | t.Fatalf("no event returned") 47 | } 48 | 49 | eventJson, err := json.MarshalIndent(eventData, "", " ") 50 | if err != nil { 51 | t.Fatalf("error converting event data to JSON: %s", err) 52 | } 53 | 54 | fmt.Printf("event successfully retrieved \n %s", string(eventJson[:])) 55 | 56 | } 57 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023-present Coinbase Global, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package commerce 18 | 19 | import ( 20 | "encoding/json" 21 | "io" 22 | "net/http" 23 | ) 24 | 25 | func handleErrorResponse(resp *http.Response) error { 26 | if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultiStatus { 27 | return nil 28 | } 29 | 30 | chargeErr := &ChargeError{} 31 | body, err := io.ReadAll(resp.Body) 32 | if err != nil { 33 | return err 34 | } 35 | if err := json.Unmarshal(body, chargeErr); err != nil { 36 | return err 37 | } 38 | return &CommerceError{ApiError: chargeErr} 39 | } 40 | --------------------------------------------------------------------------------