From 843672dfc74581b770dc7b3fc369d9cbf7545656 Mon Sep 17 00:00:00 2001 From: LeonarthusMectus Date: Wed, 16 Oct 2024 10:34:22 -0400 Subject: [PATCH] update Python Flask/Ruby Sinatra examples (#160) --- .../v2/server/python/.flaskenv | 1 + .../v2/server/python/README.md | 41 +++++++++ .../v2/server/python/requirements.txt | 2 + .../v2/server/python/server.py | 86 +++++++++++++++++++ .../v2/server/ruby/.ruby-version | 1 + advanced-integration/v2/server/ruby/Gemfile | 9 ++ advanced-integration/v2/server/ruby/README.md | 37 ++++++++ advanced-integration/v2/server/ruby/server.rb | 67 +++++++++++++++ standard-integration/server/python/.flaskenv | 1 + standard-integration/server/python/README.md | 41 +++++++++ .../server/python/requirements.txt | 2 + standard-integration/server/python/server.py | 86 +++++++++++++++++++ .../server/ruby/.ruby-version | 1 + standard-integration/server/ruby/Gemfile | 9 ++ standard-integration/server/ruby/README.md | 37 ++++++++ standard-integration/server/ruby/server.rb | 67 +++++++++++++++ 16 files changed, 488 insertions(+) create mode 100644 advanced-integration/v2/server/python/.flaskenv create mode 100644 advanced-integration/v2/server/python/README.md create mode 100644 advanced-integration/v2/server/python/requirements.txt create mode 100644 advanced-integration/v2/server/python/server.py create mode 100644 advanced-integration/v2/server/ruby/.ruby-version create mode 100644 advanced-integration/v2/server/ruby/Gemfile create mode 100644 advanced-integration/v2/server/ruby/README.md create mode 100644 advanced-integration/v2/server/ruby/server.rb create mode 100644 standard-integration/server/python/.flaskenv create mode 100644 standard-integration/server/python/README.md create mode 100644 standard-integration/server/python/requirements.txt create mode 100644 standard-integration/server/python/server.py create mode 100644 standard-integration/server/ruby/.ruby-version create mode 100644 standard-integration/server/ruby/Gemfile create mode 100644 standard-integration/server/ruby/README.md create mode 100644 standard-integration/server/ruby/server.rb diff --git a/advanced-integration/v2/server/python/.flaskenv b/advanced-integration/v2/server/python/.flaskenv new file mode 100644 index 00000000..c6cbe150 --- /dev/null +++ b/advanced-integration/v2/server/python/.flaskenv @@ -0,0 +1 @@ +FLASK_RUN_PORT=8080 \ No newline at end of file diff --git a/advanced-integration/v2/server/python/README.md b/advanced-integration/v2/server/python/README.md new file mode 100644 index 00000000..de523758 --- /dev/null +++ b/advanced-integration/v2/server/python/README.md @@ -0,0 +1,41 @@ +# Standard Integration Python Flask Sample + +PayPal Standard Integration sample in Python using Flask + +## Running the sample + +1. **Setup a virtual environment** + + ```sh + python3 -m venv .venv + ``` + +1. **Install the dependencies** + + ```sh + pip install -r requirements.txt + ``` + +1. **Add your API credentials to the environment:** + + - **Windows** + + ```powershell + $env:PAYPAL_CLIENT_ID = "" + $env:PAYPAL_CLIENT_SECRET = "" + ``` + + - **Unix** + + ```bash + export PAYPAL_CLIENT_ID="" + export PAYPAL_CLIENT_SECRET="" + ``` + +1. **Run the server** + + ```sh + flask --app server run + ``` + +1. Go to [http://localhost:8080/](http://localhost:8080/) diff --git a/advanced-integration/v2/server/python/requirements.txt b/advanced-integration/v2/server/python/requirements.txt new file mode 100644 index 00000000..7a44eadf --- /dev/null +++ b/advanced-integration/v2/server/python/requirements.txt @@ -0,0 +1,2 @@ +Flask==3.0.3 +paypal-server-sdk==0.5.2 \ No newline at end of file diff --git a/advanced-integration/v2/server/python/server.py b/advanced-integration/v2/server/python/server.py new file mode 100644 index 00000000..2cf004a3 --- /dev/null +++ b/advanced-integration/v2/server/python/server.py @@ -0,0 +1,86 @@ +import logging +import os + +from flask import Flask, request +from paypalserversdk.http.auth.o_auth_2 import ClientCredentialsAuthCredentials +from paypalserversdk.logging.configuration.api_logging_configuration import LoggingConfiguration, \ + RequestLoggingConfiguration, ResponseLoggingConfiguration +from paypalserversdk.paypalserversdk_client import PaypalserversdkClient +from paypalserversdk.controllers.orders_controller import OrdersController +from paypalserversdk.models.amount_with_breakdown import AmountWithBreakdown +from paypalserversdk.models.checkout_payment_intent import CheckoutPaymentIntent +from paypalserversdk.models.order_request import OrderRequest +from paypalserversdk.models.purchase_unit_request import PurchaseUnitRequest +from paypalserversdk.api_helper import ApiHelper + +app = Flask(__name__) + +paypal_client: PaypalserversdkClient = PaypalserversdkClient( + client_credentials_auth_credentials=ClientCredentialsAuthCredentials( + o_auth_client_id=os.getenv('PAYPAL_CLIENT_ID'), + o_auth_client_secret=os.getenv('PAYPAL_CLIENT_SECRET') + ), + logging_configuration=LoggingConfiguration( + log_level=logging.INFO, + # Disable masking of sensitive headers for Sandbox testing. + # This should be set to True (the default if unset)in production. + mask_sensitive_headers=False, + request_logging_config=RequestLoggingConfiguration( + log_headers=True, + log_body=True + ), + response_logging_config=ResponseLoggingConfiguration( + log_headers=True, + log_body=True + ) + ) +) + +''' +Health check +''' +@app.route('/', methods=['GET']) +def index(): + return {"message": "Server is running"} + +orders_controller: OrdersController = paypal_client.orders + +''' +Create an order to start the transaction. + +@see https://developer.paypal.com/docs/api/orders/v2/#orders_create +''' +@app.route('/api/orders', methods=['POST']) +def create_order(): + request_body = request.get_json() + # use the cart information passed from the front-end to calculate the order amount detals + cart = request_body['cart'] + order = orders_controller.orders_create({ + "body": OrderRequest( + intent=CheckoutPaymentIntent.CAPTURE, + purchase_units=[ + PurchaseUnitRequest( + AmountWithBreakdown( + currency_code='USD', + value='100.00' + ) + ) + ] + ), + "prefer": 'return=representation' + } + ) + return ApiHelper.json_serialize(order.body) + +''' + Capture payment for the created order to complete the transaction. + + @see https://developer.paypal.com/docs/api/orders/v2/#orders_capture +''' +@app.route('/api/orders//capture', methods=['POST']) +def capture_order(order_id): + order = orders_controller.orders_capture({ + 'id': order_id, + 'prefer': 'return=representation' + }) + return ApiHelper.json_serialize(order.body) \ No newline at end of file diff --git a/advanced-integration/v2/server/ruby/.ruby-version b/advanced-integration/v2/server/ruby/.ruby-version new file mode 100644 index 00000000..fa7adc7a --- /dev/null +++ b/advanced-integration/v2/server/ruby/.ruby-version @@ -0,0 +1 @@ +3.3.5 diff --git a/advanced-integration/v2/server/ruby/Gemfile b/advanced-integration/v2/server/ruby/Gemfile new file mode 100644 index 00000000..a5b31725 --- /dev/null +++ b/advanced-integration/v2/server/ruby/Gemfile @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "paypal-server-sdk", "~> 0.5.2" +gem "puma", "~> 6.4" +gem "rackup", "~> 2.1" +gem "sinatra", "~> 4.0" +gem "sinatra-contrib", "~> 4.0" \ No newline at end of file diff --git a/advanced-integration/v2/server/ruby/README.md b/advanced-integration/v2/server/ruby/README.md new file mode 100644 index 00000000..6ad18fe2 --- /dev/null +++ b/advanced-integration/v2/server/ruby/README.md @@ -0,0 +1,37 @@ +# Standard Integration Ruby Sinatra Sample + +PayPal Standard Integration sample in Ruby using Sinatra + +## Running the sample + +1. **Ensure you have a supported Ruby version installed**: [Ruby Maintenance Branches](https://www.ruby-lang.org/en/downloads/branches/) + +1. **Install the dependencies** + + ```bash + bundle install + ``` + +1. **Add your API credentials to the environment:** + + - **Windows** + + ```powershell + $env:PAYPAL_CLIENT_ID = "" + $env:PAYPAL_CLIENT_SECRET = "" + ``` + + - **Unix** + + ```bash + export PAYPAL_CLIENT_ID="" + export PAYPAL_CLIENT_SECRET="" + ``` + +1. **Run the server** + + ```bash + bundle exec ruby server.rb + ``` + +1. Go to [http://localhost:8080/](http://localhost:8080/) diff --git a/advanced-integration/v2/server/ruby/server.rb b/advanced-integration/v2/server/ruby/server.rb new file mode 100644 index 00000000..6f0987c8 --- /dev/null +++ b/advanced-integration/v2/server/ruby/server.rb @@ -0,0 +1,67 @@ +require 'paypal_server_sdk' +require 'sinatra' +require 'sinatra/json' + +include PaypalServerSdk + +set :port, 8080 + +paypal_client = PaypalServerSdk::Client.new( + client_credentials_auth_credentials: ClientCredentialsAuthCredentials.new( + o_auth_client_id: ENV['PAYPAL_CLIENT_ID'], + o_auth_client_secret: ENV['PAYPAL_CLIENT_SECRET'] + ), + environment: Environment::SANDBOX, + logging_configuration: LoggingConfiguration.new( + mask_sensitive_headers: false, + log_level: Logger::INFO, + request_logging_config: RequestLoggingConfiguration.new( + log_headers: true, + log_body: true, + ), + response_logging_config: ResponseLoggingConfiguration.new( + log_headers: true, + log_body: true + ) + ) +) + +# Health Check +get '/' do + json :message => "Server is running" +end + +# Create an order to start the transaction. +# +# @see https://developer.paypal.com/docs/api/orders/v2/#orders_create +post "/api/orders" do + # use the cart information passed from the front-end to calculate the order amount detals + cart = JSON.parse request.body.read + order_response = paypal_client.orders.orders_create({ + 'body' => OrderRequest.new( + intent: CheckoutPaymentIntent::CAPTURE, + purchase_units: [ + PurchaseUnitRequest.new( + amount: AmountWithBreakdown.new( + currency_code: 'USD', + value: '100.00' + ) + ) + ] + ), + 'prefer' => 'return=representation' + }) + json order_response.data +end + +# Capture payment for the created order to complete the transaction. +# +# @see https://developer.paypal.com/docs/api/orders/v2/#orders_capture +post '/api/orders/:order_id/capture' do |order_id| + capture_response = paypal_client.orders.orders_capture({ + 'id' => order_id, + 'prefer' => 'return=representation' + }) + json capture_response.data +rescue ErrorException => e +end \ No newline at end of file diff --git a/standard-integration/server/python/.flaskenv b/standard-integration/server/python/.flaskenv new file mode 100644 index 00000000..c6cbe150 --- /dev/null +++ b/standard-integration/server/python/.flaskenv @@ -0,0 +1 @@ +FLASK_RUN_PORT=8080 \ No newline at end of file diff --git a/standard-integration/server/python/README.md b/standard-integration/server/python/README.md new file mode 100644 index 00000000..de523758 --- /dev/null +++ b/standard-integration/server/python/README.md @@ -0,0 +1,41 @@ +# Standard Integration Python Flask Sample + +PayPal Standard Integration sample in Python using Flask + +## Running the sample + +1. **Setup a virtual environment** + + ```sh + python3 -m venv .venv + ``` + +1. **Install the dependencies** + + ```sh + pip install -r requirements.txt + ``` + +1. **Add your API credentials to the environment:** + + - **Windows** + + ```powershell + $env:PAYPAL_CLIENT_ID = "" + $env:PAYPAL_CLIENT_SECRET = "" + ``` + + - **Unix** + + ```bash + export PAYPAL_CLIENT_ID="" + export PAYPAL_CLIENT_SECRET="" + ``` + +1. **Run the server** + + ```sh + flask --app server run + ``` + +1. Go to [http://localhost:8080/](http://localhost:8080/) diff --git a/standard-integration/server/python/requirements.txt b/standard-integration/server/python/requirements.txt new file mode 100644 index 00000000..7a44eadf --- /dev/null +++ b/standard-integration/server/python/requirements.txt @@ -0,0 +1,2 @@ +Flask==3.0.3 +paypal-server-sdk==0.5.2 \ No newline at end of file diff --git a/standard-integration/server/python/server.py b/standard-integration/server/python/server.py new file mode 100644 index 00000000..2cf004a3 --- /dev/null +++ b/standard-integration/server/python/server.py @@ -0,0 +1,86 @@ +import logging +import os + +from flask import Flask, request +from paypalserversdk.http.auth.o_auth_2 import ClientCredentialsAuthCredentials +from paypalserversdk.logging.configuration.api_logging_configuration import LoggingConfiguration, \ + RequestLoggingConfiguration, ResponseLoggingConfiguration +from paypalserversdk.paypalserversdk_client import PaypalserversdkClient +from paypalserversdk.controllers.orders_controller import OrdersController +from paypalserversdk.models.amount_with_breakdown import AmountWithBreakdown +from paypalserversdk.models.checkout_payment_intent import CheckoutPaymentIntent +from paypalserversdk.models.order_request import OrderRequest +from paypalserversdk.models.purchase_unit_request import PurchaseUnitRequest +from paypalserversdk.api_helper import ApiHelper + +app = Flask(__name__) + +paypal_client: PaypalserversdkClient = PaypalserversdkClient( + client_credentials_auth_credentials=ClientCredentialsAuthCredentials( + o_auth_client_id=os.getenv('PAYPAL_CLIENT_ID'), + o_auth_client_secret=os.getenv('PAYPAL_CLIENT_SECRET') + ), + logging_configuration=LoggingConfiguration( + log_level=logging.INFO, + # Disable masking of sensitive headers for Sandbox testing. + # This should be set to True (the default if unset)in production. + mask_sensitive_headers=False, + request_logging_config=RequestLoggingConfiguration( + log_headers=True, + log_body=True + ), + response_logging_config=ResponseLoggingConfiguration( + log_headers=True, + log_body=True + ) + ) +) + +''' +Health check +''' +@app.route('/', methods=['GET']) +def index(): + return {"message": "Server is running"} + +orders_controller: OrdersController = paypal_client.orders + +''' +Create an order to start the transaction. + +@see https://developer.paypal.com/docs/api/orders/v2/#orders_create +''' +@app.route('/api/orders', methods=['POST']) +def create_order(): + request_body = request.get_json() + # use the cart information passed from the front-end to calculate the order amount detals + cart = request_body['cart'] + order = orders_controller.orders_create({ + "body": OrderRequest( + intent=CheckoutPaymentIntent.CAPTURE, + purchase_units=[ + PurchaseUnitRequest( + AmountWithBreakdown( + currency_code='USD', + value='100.00' + ) + ) + ] + ), + "prefer": 'return=representation' + } + ) + return ApiHelper.json_serialize(order.body) + +''' + Capture payment for the created order to complete the transaction. + + @see https://developer.paypal.com/docs/api/orders/v2/#orders_capture +''' +@app.route('/api/orders//capture', methods=['POST']) +def capture_order(order_id): + order = orders_controller.orders_capture({ + 'id': order_id, + 'prefer': 'return=representation' + }) + return ApiHelper.json_serialize(order.body) \ No newline at end of file diff --git a/standard-integration/server/ruby/.ruby-version b/standard-integration/server/ruby/.ruby-version new file mode 100644 index 00000000..fa7adc7a --- /dev/null +++ b/standard-integration/server/ruby/.ruby-version @@ -0,0 +1 @@ +3.3.5 diff --git a/standard-integration/server/ruby/Gemfile b/standard-integration/server/ruby/Gemfile new file mode 100644 index 00000000..a5b31725 --- /dev/null +++ b/standard-integration/server/ruby/Gemfile @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "paypal-server-sdk", "~> 0.5.2" +gem "puma", "~> 6.4" +gem "rackup", "~> 2.1" +gem "sinatra", "~> 4.0" +gem "sinatra-contrib", "~> 4.0" \ No newline at end of file diff --git a/standard-integration/server/ruby/README.md b/standard-integration/server/ruby/README.md new file mode 100644 index 00000000..6ad18fe2 --- /dev/null +++ b/standard-integration/server/ruby/README.md @@ -0,0 +1,37 @@ +# Standard Integration Ruby Sinatra Sample + +PayPal Standard Integration sample in Ruby using Sinatra + +## Running the sample + +1. **Ensure you have a supported Ruby version installed**: [Ruby Maintenance Branches](https://www.ruby-lang.org/en/downloads/branches/) + +1. **Install the dependencies** + + ```bash + bundle install + ``` + +1. **Add your API credentials to the environment:** + + - **Windows** + + ```powershell + $env:PAYPAL_CLIENT_ID = "" + $env:PAYPAL_CLIENT_SECRET = "" + ``` + + - **Unix** + + ```bash + export PAYPAL_CLIENT_ID="" + export PAYPAL_CLIENT_SECRET="" + ``` + +1. **Run the server** + + ```bash + bundle exec ruby server.rb + ``` + +1. Go to [http://localhost:8080/](http://localhost:8080/) diff --git a/standard-integration/server/ruby/server.rb b/standard-integration/server/ruby/server.rb new file mode 100644 index 00000000..6f0987c8 --- /dev/null +++ b/standard-integration/server/ruby/server.rb @@ -0,0 +1,67 @@ +require 'paypal_server_sdk' +require 'sinatra' +require 'sinatra/json' + +include PaypalServerSdk + +set :port, 8080 + +paypal_client = PaypalServerSdk::Client.new( + client_credentials_auth_credentials: ClientCredentialsAuthCredentials.new( + o_auth_client_id: ENV['PAYPAL_CLIENT_ID'], + o_auth_client_secret: ENV['PAYPAL_CLIENT_SECRET'] + ), + environment: Environment::SANDBOX, + logging_configuration: LoggingConfiguration.new( + mask_sensitive_headers: false, + log_level: Logger::INFO, + request_logging_config: RequestLoggingConfiguration.new( + log_headers: true, + log_body: true, + ), + response_logging_config: ResponseLoggingConfiguration.new( + log_headers: true, + log_body: true + ) + ) +) + +# Health Check +get '/' do + json :message => "Server is running" +end + +# Create an order to start the transaction. +# +# @see https://developer.paypal.com/docs/api/orders/v2/#orders_create +post "/api/orders" do + # use the cart information passed from the front-end to calculate the order amount detals + cart = JSON.parse request.body.read + order_response = paypal_client.orders.orders_create({ + 'body' => OrderRequest.new( + intent: CheckoutPaymentIntent::CAPTURE, + purchase_units: [ + PurchaseUnitRequest.new( + amount: AmountWithBreakdown.new( + currency_code: 'USD', + value: '100.00' + ) + ) + ] + ), + 'prefer' => 'return=representation' + }) + json order_response.data +end + +# Capture payment for the created order to complete the transaction. +# +# @see https://developer.paypal.com/docs/api/orders/v2/#orders_capture +post '/api/orders/:order_id/capture' do |order_id| + capture_response = paypal_client.orders.orders_capture({ + 'id' => order_id, + 'prefer' => 'return=representation' + }) + json capture_response.data +rescue ErrorException => e +end \ No newline at end of file