`
- )
+app.get("/request_token", (req, res) => {
+ // Set the Auth_State cookie with a random md5 string to protect against cross-site request forgery.
+ // Auth_State will expire in 300 seconds (5 mins) after the page is loaded.
+ var state = md5(Date.now())
+ var url = basePath + `/oauth2/authorize?client_id=${process.env.SQ_APPLICATION_ID}&` + `response_type=code&` + `scope=${scopes.join('+')}` + `&state=` + state
+ content = `
+
+
+
`
+ res.cookie("Auth_State", state, { expire: Date.now() + 300000 }).render('base', {
+ content: content
+ })
});
/**
* Description:
* Serves requests from Square to your application's redirect URL
* Note that you need to set your application's Redirect URL to
- * http://localhost:8000/sandbox_callback from your application dashboard
+ * http://localhost:8000/callback from your application dashboard
*
* Query Parameters:
* state: the Auth State set in request_token
* response_type: the type of the response; should be "code"
* code: the authorization code
*/
-app.get('/sandbox_callback', async (req, res) => {
- console.log(req.query);
- // Verify the state to protect against cross-site request forgery.
- if (req.cookies["Auth_State"] !== req.query['state']) {
- res.send(messages.displayStateError());
- }
-
- else if (req.query['error']) {
- // Check to see if the seller clicked the Deny button and handle it as a special case.
- if(("access_denied" === req.query['error']) && ("user_denied" === req.query["error_description"])) {
- res.send(messages.displayError("Authorization denied", "You chose to deny access to the app."));
- }
- // Display the error and description for all other errors.
- else {
- res.send(messages.displayError(req.query["error"], req.query["error_description"]));
- }
- }
- // When the response_type is "code", the seller clicked Allow
- // and the authorization page returned the auth tokens.
- else if ("code" === req.query["response_type"]) {
- // Extract the returned authorization code from the URL
- var { code } = req.query;
-
- try {
- let { result } = await oauthInstance.obtainToken({
- // Provide the code in a request to the Obtain Token endpoint
- code,
- clientId: process.env.SQ_SANDBOX_APP_ID,
- clientSecret: process.env.SQ_SANDBOX_APP_SECRET,
- grantType: 'authorization_code'
- });
-
- let {
- // Extract the returned access token from the ObtainTokenResponse object
- accessToken,
- refreshToken,
- expiresAt,
- merchantId
- } = result;
-
- // Because we want to keep things simple and we're using Sandbox,
- // we call a function that writes the tokens to the page so we can easily copy and use them directly.
- // In production, you should never write tokens to the page. You should encrypt the tokens and handle them securely.
- res.send(messages.writeTokensOnSuccess(accessToken, refreshToken, expiresAt, merchantId));
- } catch (error) {
- // The response from the Obtain Token endpoint did not include an access token. Something went wrong.
- if (error instanceof ApiError) {
- res.send(messages.displayError('Exception', JSON.stringify(error.result)));
- } else {
- res.send(messages.displayError('Exception', JSON.stringify(error)));
- }
- }
+app.get('/callback', async (req, res) => {
+ console.log(req.query);
+ // Verify the state to protect against cross-site request forgery.
+ if (req.cookies["Auth_State"] !== req.query['state']) {
+ content = messages.displayStateError();
+ res.render('base', {
+ content: content
+ });
+ }
+
+ else if (req.query['error']) {
+ // Check to see if the seller clicked the Deny button and handle it as a special case.
+ if (("access_denied" === req.query['error']) && ("user_denied" === req.query["error_description"])) {
+ res.render(messages.displayError("Authorization denied", "You chose to deny access to the app."));
}
+ // Display the error and description for all other errors.
else {
- // No recognizable parameters were returned.
- res.send(messages.displayError("Unknown parameters", "Expected parameters were not returned"));
+ content = messages.displayError(req.query["error"], req.query["error_description"])
+ res.render('base', {
+ content: content
+ });
+ }
+ }
+ // When the response_type is "code", the seller clicked Allow
+ // and the authorization page returned the auth tokens.
+ else if ("code" === req.query["response_type"]) {
+ // Extract the returned authorization code from the URL
+ var { code } = req.query;
+
+ try {
+ let { result } = await oauthInstance.obtainToken({
+ // Provide the code in a request to the Obtain Token endpoint
+ code,
+ clientId: process.env.SQ_APPLICATION_ID,
+ clientSecret: process.env.SQ_APPLICATION_SECRET,
+ grantType: 'authorization_code'
+ });
+
+ let {
+ // Extract the returned access token from the ObtainTokenResponse object
+ accessToken,
+ refreshToken,
+ expiresAt,
+ merchantId
+ } = result;
+
+ // Because we want to keep things simple and we're using Sandbox,
+ // we call a function that writes the tokens to the page so we can easily copy and use them directly.
+ // In production, you should never write tokens to the page. You should encrypt the tokens and handle them securely.
+ content = messages.writeTokensOnSuccess(accessToken, refreshToken, expiresAt, merchantId)
+ res.render('base', {
+ content: content
+ });
+ } catch (error) {
+ // The response from the Obtain Token endpoint did not include an access token. Something went wrong.
+ if (error instanceof ApiError) {
+ content = messages.displayError('Exception', JSON.stringify(error.result))
+ res.render('base', {
+ content: content
+ });
+ } else {
+ content = messages.displayError('Exception', JSON.stringify(error))
+ res.render('base', {
+ content: content
+ });
+ }
}
+ }
+ else {
+ // No recognizable parameters were returned.
+ content = messages.displayError("Unknown parameters", "Expected parameters were not returned")
+ res.render('base', {
+ content: content
+ });
+ }
});
app.listen(port, () => {
- console.log(`Listening to requests on http://localhost:${port}`);
+ console.log(`Listening to requests on http://localhost:${port}`);
});
diff --git a/connect-examples/oauth/node/sandbox-messages.js b/connect-examples/oauth/node/messages.js
similarity index 50%
rename from connect-examples/oauth/node/sandbox-messages.js
rename to connect-examples/oauth/node/messages.js
index 545bf9a26..98959ec93 100644
--- a/connect-examples/oauth/node/sandbox-messages.js
+++ b/connect-examples/oauth/node/messages.js
@@ -18,41 +18,62 @@ limitations under the License.
// WARNING: NEVER store or share OAuth access tokens or refresh tokens in clear text.
function writeTokensOnSuccess(access_token, refresh_token, expires_at, merchant_id) {
return (
- `
-
Authorization succeeded
-
- Caution: NEVER store or share OAuth access tokens or refresh tokens in clear text.
+ `
+
+
+
+
+
Authorization Succeeded
+
Caution: NEVER store or share OAuth access tokens or refresh tokens in clear text.
Use a strong encryption standard such as AES to encrypt OAuth tokens. Ensure the production encryption key is not
accessible to anyone who does not need it.
-
-
OAuth access token: ${access_token}
-
OAuth access token expires at: ${expires_at}
-
OAuth refresh token: ${refresh_token}
-
Merchant Id: ${merchant_id}
-
You can this OAuth access token in Sandbox to call Create Payment and other APIs that were authorized by this seller.
+
+
+
OAuth access token: ${access_token}
+
OAuth access token expires at: ${expires_at}
+
OAuth refresh token: ${refresh_token}
+
Merchant Id: ${merchant_id}
+
You can use this OAuth access token to call Create Payment and other APIs that were authorized by this seller.
+ `
+ )
}
// Display error message if the state doesn't match the state originally passed to the authorization page.
function displayStateError() {
return (
- `
Authorization failed
-
Invalid state parameter.
`
+ `
+
+
+
+
+
Authorization failed
+
Invalid state parameter.
+
+
+ `
)
}
// Display error if access token not acquired
function displayError(error, error_description) {
- return (
- `
-
Caution: NEVER store or share OAuth access tokens or refresh tokens in clear text.
+ Use a strong encryption standard such as AES to encrypt OAuth tokens. Ensure the production encryption key is not
+ accessible to anyone who does not need it.
+
+
+
OAuth access token: = $accessToken ?>
+
OAuth access token expires at: = $expiresAt ?>
+
OAuth refresh token: = $refreshToken ?>
+
Merchant Id: = $merchantId ?>
+
You can use this OAuth access token to call Create Payment and other APIs that were authorized by this seller.
Caution: NEVER store or share OAuth access tokens or refresh tokens in clear text.
- Use a strong encryption standard such as AES to encrypt OAuth tokens. Ensure the production encryption key is not
- accessible to anyone who does not need it.
-
-
OAuth access token: = $accessToken ?>
-
OAuth access token expires at: = $expiresAt ?>
-
OAuth refresh token: = $refreshToken ?>
-
Merchant Id: = $merchantId ?>
-
You can this OAuth access token in Sandbox to call Create Payment and other APIs that were authorized by this seller.
-
\ No newline at end of file
diff --git a/connect-examples/oauth/php/sandbox_request_token.php b/connect-examples/oauth/php/sandbox_request_token.php
deleted file mode 100644
index 2972de7a0..000000000
--- a/connect-examples/oauth/php/sandbox_request_token.php
+++ /dev/null
@@ -1,24 +0,0 @@
-SANDBOX: Authorize this application
';
-?>
\ No newline at end of file
diff --git a/connect-examples/oauth/python/.env.example b/connect-examples/oauth/python/.env.example
new file mode 100644
index 000000000..0e66f23f4
--- /dev/null
+++ b/connect-examples/oauth/python/.env.example
@@ -0,0 +1,3 @@
+SQ_ENVIRONMENT=your-environment
+SQ_APPLICATION_ID=your-application-id
+SQ_APPLICATION_SECRET=your-application-secret
\ No newline at end of file
diff --git a/connect-examples/oauth/python/.gitignore b/connect-examples/oauth/python/.gitignore
new file mode 100644
index 000000000..d50a09fc9
--- /dev/null
+++ b/connect-examples/oauth/python/.gitignore
@@ -0,0 +1,2 @@
+.env
+__pycache__/
diff --git a/connect-examples/oauth/python/README.md b/connect-examples/oauth/python/README.md
index 99ea83b16..72f1e531d 100644
--- a/connect-examples/oauth/python/README.md
+++ b/connect-examples/oauth/python/README.md
@@ -16,20 +16,26 @@ pip install -r requirements.txt
This application requires Python and the Flask web framework.
-### Specify your application credentials
-
-In order for the sample to work, you must specify two fields in `oauth-flow.py`:
-
-* Replace the value of `application_id` with your application's ID, available on your
-[application dashboard](https://developer.squareup.com/apps).
-
-* Replace the value of `application_secret` with your application's secret, also available on your application dashboard.
-
-### Set your application's Redirect URL
-
-On your application dashboard, set your application's Redirect URL to `http://127.0.0.1:5000/callback`.
-
-Note that applications that don't use a `localhost` URL must use HTTPS. HTTP is allowed for `localhost` URLs to simplify the development process.
+### Get your credentials and set the redirect URL:
+
+1. Open the [Developer Dashboard](https://developer.squareup.com/apps).
+1. Choose **Open** on the card for an application.
+1. At the top of the page, set the dashboard mode to the environment that you want to work with by choosing **Sandbox** or **Production**.
+1. Choose **OAuth** in the left navigation pane. The OAuth page is shown.
+1. In the **Redirect URL** box, enter the URL for the callback you will implement to complete the OAuth flow:
+ `http://127.0.0.1:5000/callback`
+
+ You can use HTTP for localhost but an actual web server implementation must use HTTPS.
+1. In the **Application ID** box, copy the application ID.
+1. In the **Application Secret** box, choose **Show**, and then copy the application secret.
+1. Click **Save**.
+1. In your project directory, create a copy of the `.env.example` file and name it `.env`
+1. In the newly created .env file, replace the `your-environment` with either `sandbox` or `production`
+1. Replace the `your-application-id` and `your-application-secret` placeholders with the Sandbox or Production application ID and application secret, respectively.
+
+ Note that OAuth Sandbox credentials begin with a sandbox prefix and that the base URL for calling Sandbox endpoints is https://connect.squareupsandbox.com. When you implement for production, you need production credentials and use https://connect.squareup.com as the base URL.
+
+ **WARNING**: Never check your credentials/access_token into your version control system. We've added `.env` to the `.gitignore` file to help prevent uploading confidential information.
## Running the example
diff --git a/connect-examples/oauth/python/oauth-flow.py b/connect-examples/oauth/python/oauth-flow.py
index b38503569..6adeb1444 100644
--- a/connect-examples/oauth/python/oauth-flow.py
+++ b/connect-examples/oauth/python/oauth-flow.py
@@ -11,25 +11,39 @@
# 5. The Obtain Token endpoint returns an access token your application can use in subsequent requests
# to the Connect API.
-from flask import Flask, request
+from flask import Flask, request, render_template
from square.client import Client
+from dotenv import load_dotenv
+import os;
+load_dotenv() # take environment variables from .env.
+
+environment = os.getenv('SQ_ENVIRONMENT').lower()
client = Client(
- environment="production"
+ environment = environment
)
obtain_token = client.o_auth.obtain_token
app = Flask(__name__)
# Your application's ID and secret, available from your application dashboard.
-application_id = 'REPLACE_ME'
-application_secret = 'REPLACE_ME'
+application_id = os.getenv('SQ_APPLICATION_ID')
+application_secret = os.getenv('SQ_APPLICATION_SECRET')
+
+base_url = "https://connect.squareup.com" if environment == "production" else "https://connect.squareupsandbox.com"
# Serves the link that merchants click to authorize your application
@app.route('/', methods=['GET'])
def authorize():
- return '''Click here
- to authorize the application.'''.format(application_id)
+ url = "{0}/oauth2/authorize?client_id={1}".format(base_url, application_id)
+ content = """
+
""".format(url)
+ return render_template("base.html", content=content)
# Serves requsts from Square to your application's redirect URL
# Note that you need to set your application's Redirect URL to
@@ -55,12 +69,46 @@ def callback():
# Here, instead of printing the access token, your application server should store it securely
# and use it in subsequent requests to the Connect API on behalf of the merchant.
print (response.body)
- return 'Authorization succeeded!'
-
+ content = """
+
+
+
Authorization Succeeded
+
Caution: NEVER store or share OAuth access tokens or refresh tokens in clear text.
+ Use a strong encryption standard such as AES to encrypt OAuth tokens. Ensure the production encryption key is not
+ accessible to anyone who does not need it.
+
+
+
OAuth access token: {}
+
OAuth access token expires at: {}
+
OAuth refresh token: {}
+
Merchant Id: {}
+
You can use this OAuth access token to call Create Payment and other APIs that were authorized by this seller.
+ """.format(response.body['access_token'], response.body['expires_at'], response.body['refresh_token'], response.body['merchant_id'])
+ return render_template("base.html", content=content)
# The response from the Obtain Token endpoint did not include an access token. Something went wrong.
else:
- return 'Code exchange failed!'
+ content = """
+
+
+
+
+
Code exchange failed
+
+
"""
+ return render_template("base.html", content=content)
# The request to the Redirect URL did not include an authorization code. Something went wrong.
else:
- return 'Authorization failed!'
+ content = """
+
+
+
+
+
Authorization failed
+
+
"""
+ return render_template("base.html", content=content)
diff --git a/connect-examples/oauth/python/requirements.txt b/connect-examples/oauth/python/requirements.txt
index 26ac15854..ec72ccbb3 100644
--- a/connect-examples/oauth/python/requirements.txt
+++ b/connect-examples/oauth/python/requirements.txt
@@ -1,2 +1,3 @@
flask
squareup
+python-dotenv
diff --git a/connect-examples/oauth/python/static/style.css b/connect-examples/oauth/python/static/style.css
new file mode 100644
index 000000000..6ee0603a5
--- /dev/null
+++ b/connect-examples/oauth/python/static/style.css
@@ -0,0 +1,127 @@
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 300;
+ font-style: normal;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-light-b61b564a07d65d68488de3231bd0ac7b.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-light-5cb2f5f8f691834beba1bfc2ec90b2d2.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 300;
+ font-style: italic;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-light-italic-ecd38219bc03bfcec03e0c041c2b6036.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-light-italic-9457b0534d03b93e1cdd78f7dccc8c63.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 400;
+ font-style: normal;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-regular-84eba500a4ec29ccfadf1e9bff16b67f.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-regular-9ad7ff5c9923221c0bb0cb3bb07fe118.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 400;
+ font-style: italic;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-regular-italic-acd95aa5e04a7ef2eeaacc6034354d68.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-regular-italic-49bc07c0498062dce2bfbb0a5728e328.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 500;
+ font-style: normal;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-medium-2344124773c71bf4fa4ad407e7c3a467.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-medium-87fe9beef569bb03cb3845a1e478abac.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 500;
+ font-style: italic;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-medium-italic-8fb572c8d91a409f5b0a113fea4225b9.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-medium-italic-a30c187aa93ef2d140b4e8796162258f.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Roboto Mono";
+ font-weight: 400;
+ font-style: normal;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/robotomono-regular-608bba1cd36d8adf4aaa4fac946df3db.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/robotomono-regular-2b672f0eafb496a00a7fe8a32790f72e.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Roboto Mono";
+ font-weight: 600;
+ font-style: bold;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/robotomono-bold-8f798852f4b39b8250cd416963df1952.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/robotomono-bold-77106546c8c8a7ec5289a2a0b9cbe7c0.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: Roboto;
+ font-weight: 400;
+ font-style: normal;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/roboto-regular-20fc6d9b68d397a5f60b8607d05c2dca.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/roboto-regular-ece4ca3c4fda346f98adc6184df6ed73.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: Roboto;
+ font-weight: 500;
+ font-style: bold;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/roboto-medium-a6d6531b28ed13dee1295f82389a3526.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/roboto-medium-715829cbe4bc43e24373673931e699bf.woff) format("woff")
+}
+
+html {
+ color: #151c1f;
+ font-family: "Square Market", "Helvetica Neue", Helvetica, sans-serif;
+ font-size: 16px;
+ line-height: 24px;
+ -webkit-font-smoothing: antialiased
+}
+
+h1 {
+ color: rgba(0, 95, 229, 1);
+}
+
+a {
+ color: rgba(0, 106, 255, 1);
+ text-decoration: none;
+}
+
+a:hover {
+ color: rgba(0, 95, 229, 1);
+}
+
+.wrapper {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.messages {
+ max-width: 700px;
+ padding: 40px;
+ background-color: rgba(0, 0, 0, 0.02);
+}
+
+.btn {
+ background-color: rgba(0, 106, 255, 1);
+ color: white;
+ border-radius: 6px;
+ padding: 20px 16px 20px 16px;
+ transition: background-color .2s ease, color .2s ease;
+}
+
+.btn:hover {
+ background-color: rgba(0, 85, 204, 1);
+ color: white;
+}
\ No newline at end of file
diff --git a/connect-examples/oauth/python/templates/base.html b/connect-examples/oauth/python/templates/base.html
new file mode 100644
index 000000000..f8b27338f
--- /dev/null
+++ b/connect-examples/oauth/python/templates/base.html
@@ -0,0 +1,4 @@
+
+
+
+{{content|safe}}
\ No newline at end of file
diff --git a/connect-examples/oauth/ruby/.env.example b/connect-examples/oauth/ruby/.env.example
new file mode 100644
index 000000000..0e66f23f4
--- /dev/null
+++ b/connect-examples/oauth/ruby/.env.example
@@ -0,0 +1,3 @@
+SQ_ENVIRONMENT=your-environment
+SQ_APPLICATION_ID=your-application-id
+SQ_APPLICATION_SECRET=your-application-secret
\ No newline at end of file
diff --git a/connect-examples/oauth/ruby/.gitignore b/connect-examples/oauth/ruby/.gitignore
new file mode 100644
index 000000000..4c49bd78f
--- /dev/null
+++ b/connect-examples/oauth/ruby/.gitignore
@@ -0,0 +1 @@
+.env
diff --git a/connect-examples/oauth/ruby/Gemfile b/connect-examples/oauth/ruby/Gemfile
index ba2e24950..546994e41 100644
--- a/connect-examples/oauth/ruby/Gemfile
+++ b/connect-examples/oauth/ruby/Gemfile
@@ -3,3 +3,4 @@ source "https://rubygems.org"
ruby '2.4.2'
gem 'square.rb'
gem 'sinatra', '~> 2.0'
+gem 'dotenv-rails', groups: [:development, :test]
\ No newline at end of file
diff --git a/connect-examples/oauth/ruby/Gemfile.lock b/connect-examples/oauth/ruby/Gemfile.lock
index 357e8f33a..b76d72f54 100644
--- a/connect-examples/oauth/ruby/Gemfile.lock
+++ b/connect-examples/oauth/ruby/Gemfile.lock
@@ -1,24 +1,74 @@
GEM
remote: https://rubygems.org/
specs:
+ actionpack (5.2.5)
+ actionview (= 5.2.5)
+ activesupport (= 5.2.5)
+ rack (~> 2.0, >= 2.0.8)
+ rack-test (>= 0.6.3)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
+ actionview (5.2.5)
+ activesupport (= 5.2.5)
+ builder (~> 3.1)
+ erubi (~> 1.4)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
+ activesupport (5.2.5)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ builder (3.2.4)
certifi (2018.01.18)
+ concurrent-ruby (1.1.8)
+ crass (1.0.6)
+ dotenv (2.7.6)
+ dotenv-rails (2.7.6)
+ dotenv (= 2.7.6)
+ railties (>= 3.2)
+ erubi (1.10.0)
faraday (0.17.3)
multipart-post (>= 1.2, < 3)
faraday-http-cache (2.2.0)
faraday (>= 0.8)
faraday_middleware (0.14.0)
faraday (>= 0.7.4, < 1.0)
+ i18n (1.8.10)
+ concurrent-ruby (~> 1.0)
little-plugger (1.1.4)
logging (2.3.0)
little-plugger (~> 1.1)
multi_json (~> 1.14)
+ loofah (2.9.1)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ method_source (1.0.0)
+ mini_portile2 (2.4.0)
+ minitest (5.14.4)
multi_json (1.15.0)
multipart-post (2.1.1)
mustermann (1.1.1)
ruby2_keywords (~> 0.0.1)
+ nokogiri (1.10.10)
+ mini_portile2 (~> 2.4.0)
rack (2.2.3)
rack-protection (2.0.8.1)
rack
+ rack-test (1.1.0)
+ rack (>= 1.0, < 3)
+ rails-dom-testing (2.0.3)
+ activesupport (>= 4.2.0)
+ nokogiri (>= 1.6)
+ rails-html-sanitizer (1.3.0)
+ loofah (~> 2.3)
+ railties (5.2.5)
+ actionpack (= 5.2.5)
+ activesupport (= 5.2.5)
+ method_source
+ rake (>= 0.8.7)
+ thor (>= 0.19.0, < 2.0)
+ rake (13.0.3)
ruby2_keywords (0.0.2)
sinatra (2.0.8.1)
mustermann (~> 1.0)
@@ -31,12 +81,17 @@ GEM
faraday-http-cache (~> 2.0)
faraday_middleware (~> 0.13)
logging (~> 2.2)
+ thor (1.1.0)
+ thread_safe (0.3.6)
tilt (2.0.10)
+ tzinfo (1.2.9)
+ thread_safe (~> 0.1)
PLATFORMS
ruby
DEPENDENCIES
+ dotenv-rails
sinatra (~> 2.0)
square.rb
diff --git a/connect-examples/oauth/ruby/README.md b/connect-examples/oauth/ruby/README.md
index 316ae4606..ca3ada46d 100644
--- a/connect-examples/oauth/ruby/README.md
+++ b/connect-examples/oauth/ruby/README.md
@@ -2,7 +2,7 @@
This example demonstrates a bare-bones Ruby implementation of the Square Connect OAuth flow. The application links merchants to the OAuth Permissions form and handles the result of the authorization, which is sent to the application's Redirect URL.
-For more information, see [OAuth Overview](https://docs.connect.squareup.com/api/oauth#oauth-overview), along with the comments included in `oauth-flow.rb`.
+For more information, see [OAuth Overview](https://developer.squareup.com/docs/oauth-api/overview), along with the comments included in `oauth-flow.rb`.
## Setup
@@ -11,20 +11,26 @@ For more information, see [OAuth Overview](https://docs.connect.squareup.com/api
This application requires gems for the Sinatra web framework and Square Connect
Install them with `bundle install`
-### Specify your application credentials
+### Get your credentials and set the redirect URL:
-In order for the sample to work, you must specify two fields in `oauth-flow.rb`:
+1. Open the [Developer Dashboard](https://developer.squareup.com/apps).
+1. Choose **Open** on the card for an application.
+1. At the top of the page, set the dashboard mode to the environment that you want to work with by choosing **Sandbox** or **Production**.
+1. Choose **OAuth** in the left navigation pane. The OAuth page is shown.
+1. In the **Redirect URL** box, enter the URL for the callback you will implement to complete the OAuth flow:
+ `http://localhost:4567/callback`
-* Replace the value of `$application_id` with your application's ID, available on your
-[application dashboard](https://connect.squareup.com/apps).
+ You can use HTTP for localhost but an actual web server implementation must use HTTPS.
+1. In the **Application ID** box, copy the application ID.
+1. In the **Application Secret** box, choose **Show**, and then copy the application secret.
+1. Click **Save**.
+1. In your project directory, create a copy of the `.env.example` file and name it `.env`
+1. In the newly created .env file, replace the `your-environment` with either `sandbox` or `production`
+1. Replace the `your-application-id` and `your-application-secret` placeholders with the Sandbox or Production application ID and application secret, respectively.
-* Replace the value of `$application_secret` with your application's secret, also available on your application dashboard.
+ Note that OAuth Sandbox credentials begin with a sandbox prefix and that the base URL for calling Sandbox endpoints is https://connect.squareupsandbox.com. When you implement for production, you need production credentials and use https://connect.squareup.com as the base URL.
-### Set your application's Redirect URL
-
-On your application dashboard, set your application's Redirect URL to `http://localhost:4567/callback`.
-
-Note that applications that don't use a `localhost` URL must use HTTPS. HTTP is allowed for `localhost` URLs to simplify the development process.
+ **WARNING**: Never check your credentials/access_token into your version control system. We've added `.env` to the `.gitignore` file to help prevent uploading confidential information.
## Running the example
diff --git a/connect-examples/oauth/ruby/oauth-flow.rb b/connect-examples/oauth/ruby/oauth-flow.rb
index b3af436e4..e6ee33379 100644
--- a/connect-examples/oauth/ruby/oauth-flow.rb
+++ b/connect-examples/oauth/ruby/oauth-flow.rb
@@ -16,19 +16,37 @@
require 'sinatra'
require 'square'
+require 'dotenv/load'
# Your application's ID and secret, available from your application dashboard.
-APP_ID = 'REPLACE_ME'
-APP_SECRET = 'REPLACE_ME'
+application_id = ENV['SQ_APPLICATION_ID']
+application_secret = ENV['SQ_APPLICATION_SECRET']
+environment = ENV['SQ_ENVIRONMENT'].downcase
-CONNECT_HOST = "https://connect.squareup.com"
+if environment == 'production'
+ connect_host = "https://connect.squareup.com"
+else
+ connect_host = "https://connect.squareupsandbox.com"
+end
-oauth_api = Square::Client.new.o_auth
+client = Square::Client.new(
+ environment: environment
+)
+oauth_api = client.o_auth
# Serves the link that merchants click to authorize your application
get '/' do
- "Click here
- to authorize the application."
+ url = "#{connect_host}/oauth2/authorize?client_id=#{application_id}"
+ content = "
+
+
+
"
+ erb :base, :locals => {:content => content}
end
# Serves requsts from Square to your application's redirect URL
@@ -43,8 +61,8 @@
# Provide the code in a request to the Obtain Token endpoint
oauth_request_body = {
- 'client_id' => APP_ID,
- 'client_secret' => APP_SECRET,
+ 'client_id' => application_id,
+ 'client_secret' => application_secret,
'code' => authorization_code,
'grant_type' => 'authorization_code'
}
@@ -54,18 +72,49 @@
# Extract the returned access token from the ObtainTokenResponse object
if response.success?
- # Here, instead of printing the access token, your application server should store it securely
+ # In production, instead of printing the access token, your application server should store it securely
# and use it in subsequent requests to the Connect API on behalf of the merchant.
puts 'Access token: ' + response.data.access_token
- return 'Authorization succeeded!'
-
+ content = "
+
+
+
Authorization Succeeded
+
Caution: NEVER store or share OAuth access tokens or refresh tokens in clear text.
+ Use a strong encryption standard such as AES to encrypt OAuth tokens. Ensure the production encryption key is not
+ accessible to anyone who does not need it.
+
+ "
# The response from the Obtain Token endpoint did not include an access token. Something went wrong.
else
- return 'Code exchange failed!'
+ content = "
+
+
+
+
+
Code exchange failed
+
+
"
end
-
# The request to the Redirect URL did not include an authorization code. Something went wrong.
else
- return 'Authorization failed!'
+ content = "
+
+
+
+
+
Authorization failed
+
+
"
end
+ return erb :base, :locals => {:content => content}
end
diff --git a/connect-examples/oauth/ruby/public/style.css b/connect-examples/oauth/ruby/public/style.css
new file mode 100644
index 000000000..6ee0603a5
--- /dev/null
+++ b/connect-examples/oauth/ruby/public/style.css
@@ -0,0 +1,127 @@
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 300;
+ font-style: normal;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-light-b61b564a07d65d68488de3231bd0ac7b.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-light-5cb2f5f8f691834beba1bfc2ec90b2d2.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 300;
+ font-style: italic;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-light-italic-ecd38219bc03bfcec03e0c041c2b6036.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-light-italic-9457b0534d03b93e1cdd78f7dccc8c63.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 400;
+ font-style: normal;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-regular-84eba500a4ec29ccfadf1e9bff16b67f.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-regular-9ad7ff5c9923221c0bb0cb3bb07fe118.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 400;
+ font-style: italic;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-regular-italic-acd95aa5e04a7ef2eeaacc6034354d68.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-regular-italic-49bc07c0498062dce2bfbb0a5728e328.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 500;
+ font-style: normal;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-medium-2344124773c71bf4fa4ad407e7c3a467.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-medium-87fe9beef569bb03cb3845a1e478abac.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Square Market";
+ font-weight: 500;
+ font-style: italic;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-medium-italic-8fb572c8d91a409f5b0a113fea4225b9.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/sqmarket-medium-italic-a30c187aa93ef2d140b4e8796162258f.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Roboto Mono";
+ font-weight: 400;
+ font-style: normal;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/robotomono-regular-608bba1cd36d8adf4aaa4fac946df3db.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/robotomono-regular-2b672f0eafb496a00a7fe8a32790f72e.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Roboto Mono";
+ font-weight: 600;
+ font-style: bold;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/robotomono-bold-8f798852f4b39b8250cd416963df1952.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/robotomono-bold-77106546c8c8a7ec5289a2a0b9cbe7c0.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: Roboto;
+ font-weight: 400;
+ font-style: normal;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/roboto-regular-20fc6d9b68d397a5f60b8607d05c2dca.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/roboto-regular-ece4ca3c4fda346f98adc6184df6ed73.woff) format("woff")
+}
+
+@font-face {
+ font-display: swap;
+ font-family: Roboto;
+ font-weight: 500;
+ font-style: bold;
+ src: url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/roboto-medium-a6d6531b28ed13dee1295f82389a3526.woff2) format("woff2"), url(https://sq-connect-web-production-f.squarecdn.com/square/forge-core/fonts/roboto-medium-715829cbe4bc43e24373673931e699bf.woff) format("woff")
+}
+
+html {
+ color: #151c1f;
+ font-family: "Square Market", "Helvetica Neue", Helvetica, sans-serif;
+ font-size: 16px;
+ line-height: 24px;
+ -webkit-font-smoothing: antialiased
+}
+
+h1 {
+ color: rgba(0, 95, 229, 1);
+}
+
+a {
+ color: rgba(0, 106, 255, 1);
+ text-decoration: none;
+}
+
+a:hover {
+ color: rgba(0, 95, 229, 1);
+}
+
+.wrapper {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.messages {
+ max-width: 700px;
+ padding: 40px;
+ background-color: rgba(0, 0, 0, 0.02);
+}
+
+.btn {
+ background-color: rgba(0, 106, 255, 1);
+ color: white;
+ border-radius: 6px;
+ padding: 20px 16px 20px 16px;
+ transition: background-color .2s ease, color .2s ease;
+}
+
+.btn:hover {
+ background-color: rgba(0, 85, 204, 1);
+ color: white;
+}
\ No newline at end of file
diff --git a/connect-examples/oauth/ruby/views/base.erb b/connect-examples/oauth/ruby/views/base.erb
new file mode 100644
index 000000000..b995f4068
--- /dev/null
+++ b/connect-examples/oauth/ruby/views/base.erb
@@ -0,0 +1,4 @@
+
+
+
+<%= content %>
\ No newline at end of file
diff --git a/connect-examples/v1/php/webhooks.php b/connect-examples/v1/php/webhooks.php
index 1969332ae..1be9a1b5d 100644
--- a/connect-examples/v1/php/webhooks.php
+++ b/connect-examples/v1/php/webhooks.php
@@ -6,7 +6,7 @@
# installation instructions.
#
# See Webhooks Overview for more information:
-# https://docs.connect.squareup.com/api/connect/v1/#webhooks-overview
+# https://developer.squareup.com/docs/webhooks-api/v1-tech-ref
require 'vendor/autoload.php';
diff --git a/connect-examples/v1/python/webhooks.py b/connect-examples/v1/python/webhooks.py
index a1b58fddc..433973a8f 100644
--- a/connect-examples/v1/python/webhooks.py
+++ b/connect-examples/v1/python/webhooks.py
@@ -3,7 +3,7 @@
# Demonstrates a server listening for webhook notifications from the Square Connect API
#
# See Webhooks Overview for more information:
-# https://docs.connect.squareup.com/api/connect/v1/#webhooks-overview
+# https://developer.squareup.com/docs/webhooks-api/v1-tech-ref
#
# To install Python on Windows:
# https://www.python.org/download/
diff --git a/connect-examples/v1/ruby/webhooks.rb b/connect-examples/v1/ruby/webhooks.rb
index 1b4c52a04..34ecfc4ce 100644
--- a/connect-examples/v1/ruby/webhooks.rb
+++ b/connect-examples/v1/ruby/webhooks.rb
@@ -1,7 +1,7 @@
# Demonstrates a Sinatra server listening for webhook notifications from the Square Connect API
#
# See Webhooks Overview for more information:
-# https://docs.connect.squareup.com/api/connect/v1/#webhooks-overview
+# https://developer.squareup.com/docs/webhooks-api/v1-tech-ref
#
# This sample requires the following gems:
# sinatra (http://www.sinatrarb.com/)
@@ -36,10 +36,10 @@
# Retrieves payments by the IDs provided in webhook notifications.
#
# Note that you need to set your application's webhook URL from your application dashboard
-# to receive these notifications. In this sample, if your host's base URL is
+# to receive these notifications. In this sample, if your host's base URL is
# http://example.com, you'd set your webhook URL to http://example.com/events.
post '/events' do
-
+
# Get the JSON body and HMAC-SHA1 signature of the incoming POST request
callback_body = request.body.string
callback_signature = request.env['HTTP_X_SQUARE_SIGNATURE']
@@ -76,7 +76,7 @@
# Validates HMAC-SHA1 signatures included in webhook notifications to ensure notifications came from Square
def is_valid_callback(callback_body, callback_signature)
-
+
# Combine your webhook notification URL and the JSON body of the incoming request into a single string
string_to_sign = WEBHOOK_URL + callback_body
diff --git a/connect-examples/v2/README.md b/connect-examples/v2/README.md
index 58d3824c2..da69cbfe9 100644
--- a/connect-examples/v2/README.md
+++ b/connect-examples/v2/README.md
@@ -1,18 +1,20 @@
This folder includes the following samples:
-* __rails_payment__: a simple rails server implementation with an accompanying payment
-form.
-* __node_payment__: a node server implementation. This example includes a single,
-standard Javascript front-end implementation.
-* __php_payment__: a simple PHP server implementation with an accompanying payment
-form.
+* __csharp_checkout__: a simple .NET server implementation that utilizes Square Checkout
+to take a custom payment.
+* __csharp_payment__: a simple .NET server implementation for taking payments with an accompanying UI.
+* __java_payment__: a simple Java server implementation for taking payments with an accompanying UI.
+* __node_invoices__: a Node.js server implementation for creating invoices.
+* __node_orders-payments__: a Node.js server implementation for taking online orders with itemization and delivery options, then making a payment. Comes with an accompanying UI.
+* __node_payment__: a Node.js server implementation for taking payments with an accompanying UI.
+* __node_subscriptions__: a Node.js server implementation for creating and managing subscriptions for a merchant.
+* __php_payment__: a simple PHP server implementation for taking payments with an accompanying UI.
* __php_checkout__: a simple PHP server implementation that utilizes Square Checkout
to take a custom payment.
-* __csharp_payment__: a simple C# server implementation with an accompanying payment
-form.
-* __python_payment__: a simple Python server implementation with an accompanying payment
-form.
+* __python_payment__: a simple Python server implementation for taking payments with an accompanying UI.
+* __rails_payment__: a simple Ruby on Rails server implementation for taking payments with an accompanying UI.
+* __rails_snippet__: a Ruby on Rails server implementation of using OAuth to add an HTML snippet to a Square Online store.
**For more information, visit the Square SDK page: https://developer.squareup.com/docs/sdks**
diff --git a/connect-examples/v2/csharp_checkout/csharp_checkout.csproj b/connect-examples/v2/csharp_checkout/csharp_checkout.csproj
index fda1d22e9..3e064d616 100644
--- a/connect-examples/v2/csharp_checkout/csharp_checkout.csproj
+++ b/connect-examples/v2/csharp_checkout/csharp_checkout.csproj
@@ -3,47 +3,47 @@
netcoreapp3.1
-
-
-
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
-
-
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/connect-examples/v2/csharp_payment/Pages/ProcessPayment.cshtml b/connect-examples/v2/csharp_payment/Pages/ProcessPayment.cshtml
index 99c168d6a..d925de50b 100644
--- a/connect-examples/v2/csharp_payment/Pages/ProcessPayment.cshtml
+++ b/connect-examples/v2/csharp_payment/Pages/ProcessPayment.cshtml
@@ -3,4 +3,3 @@
@{
ViewData["Title"] = "Payment Processed";
}
-
@Model.ResultMessage
\ No newline at end of file
diff --git a/connect-examples/v2/csharp_payment/Pages/ProcessPayment.cshtml.cs b/connect-examples/v2/csharp_payment/Pages/ProcessPayment.cshtml.cs
index 0a93fa216..8ef01b8f7 100644
--- a/connect-examples/v2/csharp_payment/Pages/ProcessPayment.cshtml.cs
+++ b/connect-examples/v2/csharp_payment/Pages/ProcessPayment.cshtml.cs
@@ -1,81 +1,84 @@
using System;
+using System.IO;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using Square;
using Square.Models;
using Square.Apis;
using Square.Exceptions;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
namespace sqRazorSample.Pages
{
- public class ProcessPaymentModel : PageModel
- {
- private SquareClient client;
- private string locationId;
-
- public string ResultMessage { get; set; }
+ public class ProcessPaymentModel : PageModel
+ {
+ private SquareClient client;
+ private string locationId;
- public ProcessPaymentModel(Microsoft.Extensions.Configuration.IConfiguration configuration) {
- // Get environment
- Square.Environment environment = configuration["AppSettings:Environment"] == "sandbox" ?
- Square.Environment.Sandbox : Square.Environment.Production;
-
- // Build base client
- client = new SquareClient.Builder()
- .Environment(environment)
- .AccessToken(configuration["AppSettings:AccessToken"])
- .Build();
+ public ProcessPaymentModel(Microsoft.Extensions.Configuration.IConfiguration configuration)
+ {
+ var environment = configuration["AppSettings:Environment"] == "sandbox" ?
+ Square.Environment.Sandbox : Square.Environment.Production;
- locationId = configuration["AppSettings:LocationId"];
- }
+ client = new SquareClient.Builder()
+ .Environment(environment)
+ .AccessToken(configuration["AppSettings:AccessToken"])
+ .Build();
- async public Task OnPost()
- {
- string nonce = Request.Form["nonce"];
- IPaymentsApi PaymentsApi = client.PaymentsApi;
- // Every payment you process with the SDK must have a unique idempotency key.
- // If you're unsure whether a particular payment succeeded, you can reattempt
- // it with the same idempotency key without worrying about double charging
- // the buyer.
- string uuid = NewIdempotencyKey();
+ locationId = configuration["AppSettings:LocationId"];
+ }
- // Get the currency for the location
- RetrieveLocationResponse locationResponse = await client.LocationsApi.RetrieveLocationAsync(locationId: locationId);
- string currency = locationResponse.Location.Currency;
+ public async Task OnPostAsync()
+ {
+ var request = JObject.Parse(await new StreamReader(Request.Body).ReadToEndAsync());
+ var token = (String)request["token"];
+ var PaymentsApi = client.PaymentsApi;
+ // Every payment you process with the SDK must have a unique idempotency key.
+ // If you're unsure whether a particular payment succeeded, you can reattempt
+ // it with the same idempotency key without worrying about double charging
+ // the buyer.
+ string uuid = NewIdempotencyKey();
- // Monetary amounts are specified in the smallest unit of the applicable currency.
- // This amount is in cents. It's also hard-coded for $1.00,
- // which isn't very useful.
- Money amount = new Money.Builder()
- .Amount(500L)
- .Currency(currency)
- .Build();
+ // Get the currency for the location
+ var retrieveLocationResponse = await client.LocationsApi.RetrieveLocationAsync(locationId: locationId);
+ var currency = retrieveLocationResponse.Location.Currency;
- // To learn more about splitting payments with additional recipients,
- // see the Payments API documentation on our [developer site]
- // (https://developer.squareup.com/docs/payments-api/overview).
- CreatePaymentRequest createPaymentRequest = new CreatePaymentRequest.Builder(nonce,uuid,amount)
- .Note("From Square Sample Csharp App")
- .Build();
+ // Monetary amounts are specified in the smallest unit of the applicable currency.
+ // This amount is in cents. It's also hard-coded for $1.00,
+ // which isn't very useful.
+ var amount = new Money.Builder()
+ .Amount(100L)
+ .Currency(currency)
+ .Build();
- try
- {
- CreatePaymentResponse response = PaymentsApi.CreatePayment(createPaymentRequest);
- ResultMessage = "Payment complete! " + response.Payment.Note;
- }
- catch (ApiException e)
- {
- ResultMessage = e.Message;
- }
+ // To learn more about splitting payments with additional recipients,
+ // see the Payments API documentation on our [developer site]
+ // (https://developer.squareup.com/docs/payments-api/overview).
+ var createPaymentRequest = new CreatePaymentRequest.Builder(
+ sourceId: token,
+ idempotencyKey: uuid,
+ amountMoney: amount)
+ .Build();
- return Page();
- }
+ try
+ {
+ var response = await PaymentsApi.CreatePaymentAsync(createPaymentRequest);
+ return new JsonResult(new { payment = response.Payment });
+ }
+ catch (ApiException e)
+ {
+ return new JsonResult(new { errors = e.Errors });
+ }
+ }
- private static string NewIdempotencyKey()
- {
- return Guid.NewGuid().ToString();
- }
+ private static string NewIdempotencyKey()
+ {
+ return Guid.NewGuid().ToString();
}
+ }
}
diff --git a/connect-examples/v2/csharp_payment/Pages/_Layout.cshtml b/connect-examples/v2/csharp_payment/Pages/_Layout.cshtml
index bf05cdbf1..81a27b928 100644
--- a/connect-examples/v2/csharp_payment/Pages/_Layout.cshtml
+++ b/connect-examples/v2/csharp_payment/Pages/_Layout.cshtml
@@ -8,7 +8,7 @@
@RenderBody()
-
+
@RenderSection("Scripts", required: false)