diff --git a/.gitignore b/.gitignore index 7846546..7f4a2a9 100644 --- a/.gitignore +++ b/.gitignore @@ -54,4 +54,8 @@ build # Ignore Docker env file docker.env +# Ignore environment file .env + +# Ignore Examples Dependencies file +examples/**/Dependencies.toml diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 6fdf7f8..7147777 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -200,6 +200,9 @@ version = "0.0.0" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] +modules = [ + {org = "ballerina", packageName = "lang.runtime", moduleName = "lang.runtime"} +] [[package]] org = "ballerina" @@ -338,6 +341,7 @@ dependencies = [ {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "data.jsondata"}, {org = "ballerina", name = "http"}, + {org = "ballerina", name = "lang.runtime"}, {org = "ballerina", name = "log"}, {org = "ballerina", name = "test"}, {org = "ballerina", name = "time"}, diff --git a/ballerina/tests/mock_service.bal b/ballerina/tests/mock_service.bal index 0469e77..11c1a53 100644 --- a/ballerina/tests/mock_service.bal +++ b/ballerina/tests/mock_service.bal @@ -17,7 +17,7 @@ import ballerina/http; import ballerina/log; -configurable boolean isLiveServer = ?; +configurable boolean isLiveServer = false; listener http:Listener httpListener = new (9090); diff --git a/ballerina/tests/sts_service.bal b/ballerina/tests/sts_service.bal new file mode 100644 index 0000000..428bd65 --- /dev/null +++ b/ballerina/tests/sts_service.bal @@ -0,0 +1,52 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; + +configurable int HTTP_SERVER_PORT = 9444; +configurable int TOKEN_VALIDITY_PERIOD = 10000; + +http:Listener stsListener = check new (HTTP_SERVER_PORT); + +http:Service sts = service object { + resource function post token() returns json { + return { + "access_token": "test-access-token", + "token_type": "example", + "expires_in": TOKEN_VALIDITY_PERIOD, + "example_parameter": "example_value" + }; + } + + resource function post introspect() returns json { + return { + "active": true, + "scope": "read write", + "client_id": "test-client-id", + "username": "test-user", + "token_type": "example", + "exp": TOKEN_VALIDITY_PERIOD, + "iat": 1419350238, + "nbf": 1419350238, + "sub": "Z5O3upPC88QrAjx00dis", + "aud": "https://protected.example.net/resource", + "iss": "https://server.example.com/", + "jti": "JlbmMiOiJBMTI4Q0JDLUhTMjU2In", + "extension_field": "twenty-seven", + "scp": "admin" + }; + } +}; diff --git a/ballerina/tests/tests.bal b/ballerina/tests/tests.bal index e102bc6..b0670b3 100644 --- a/ballerina/tests/tests.bal +++ b/ballerina/tests/tests.bal @@ -15,29 +15,49 @@ // under the License. import ballerina/http; +import ballerina/lang.runtime; +import ballerina/log; import ballerina/test; import ballerina/time; configurable string serviceUrl = isLiveServer ? "https://api-m.sandbox.paypal.com/v1/billing" : "http://localhost:9090/v1/billing"; -configurable string clientId = ?; -configurable string clientSecret = ?; -configurable string testActivatedSubscriptionId = ?; +configurable string clientId = "mock-client-id"; +configurable string clientSecret = "mock-client-secret"; +configurable string testActivatedSubscriptionId = "mock-activated-subscription-id"; string testProductId = ""; string testPlanId = ""; string testSubscriptionId = ""; string testActivatedSubscriptionPlanId = ""; +string mockTokenUrl = "http://localhost:" + HTTP_SERVER_PORT.toString() + "/oauth2/token"; ConnectionConfig config = { auth: { clientId, - clientSecret + clientSecret, + tokenUrl: isLiveServer ? "https://api-m.sandbox.paypal.com/v1/oauth2/token" : mockTokenUrl } }; -final Client paypal = check new Client(config, serviceUrl); +Client paypal = test:mock(Client); @test:BeforeSuite +function initClient() returns error? { + if isLiveServer { + paypal = check new (config, serviceUrl); + } + else { + check stsListener.attach(sts, "/oauth2"); + check stsListener.'start(); + + runtime:registerListener(stsListener); + log:printInfo(string `STS started on port: ${HTTP_SERVER_PORT} (HTTP)`); + + paypal = check new (config, serviceUrl); + } + check createProduct(); +} + function createProduct() returns error? { if !isLiveServer { testProductId = "PRD-TEMP"; @@ -47,7 +67,7 @@ function createProduct() returns error? { int timestamp = currentTime[0]; http:Client productClient = check new ("https://api-m.sandbox.paypal.com/v1/catalogs", config = { auth: { - tokenUrl: "https://api-m.sandbox.paypal.com/v1/oauth2/token", + tokenUrl: isLiveServer ? "https://api-m.sandbox.paypal.com/v1/oauth2/token" : mockTokenUrl, clientId, clientSecret } diff --git a/examples/README.md b/examples/README.md index 56970b4..86cd687 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,16 +1,23 @@ # Examples -The `ballerinax/paypal.subscriptions` connector provides practical examples illustrating usage in various scenarios. +The `ballerinax/paypal.subscriptions` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/ballerina-platform/module-ballerinax-paypal.subscriptions/tree/main/examples), covering use cases like creating and listing subscription plans, and monitoring and managing subscription status. -[//]: # (TODO: Add examples) -1. -2. +1. [Create and list plans](https://github.com/ballerina-platform/module-ballerinax-paypal.subscriptions/tree/main/examples/create-and-list-plans/create-and-list-plans.md) - Create a subscription plan and list all available plans for subscription-based services. +2. [Monitor and manage subscription status](https://github.com/ballerina-platform/module-ballerinax-paypal.subscriptions/tree/main/examples/monitor-and-manage-subscription/monitor-and-manage-subscription.md) - Retrieve a subscription's status and suspend or reactivate it based on its current state. ## Prerequisites -[//]: # (TODO: Add prerequisites) +1. Generate PayPal credentials to authenticate the connector as described in the [Setup guide](https://central.ballerina.io/ballerinax/paypal.subscriptions/latest#setup-guide). +2. For each example, create a `Config.toml` file with the related configuration. Here's an example of how your `Config.toml` file should look: -## Running an example + ```toml + clientId = "" + clientSecret = "" + productId = "" # Required for create-and-list-plans + subscriptionId = "" # Required for monitor-and-manage-subscription + ``` + +## Running an Example Execute the following commands to build an example from the source: diff --git a/examples/create-and-list-plans/Ballerina.toml b/examples/create-and-list-plans/Ballerina.toml new file mode 100644 index 0000000..21a7f9b --- /dev/null +++ b/examples/create-and-list-plans/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "wso2" +name = "create_and_list_plans" +version = "0.1.0" +distribution = "2201.12.7" + +[build-options] +observabilityIncluded = true diff --git a/examples/create-and-list-plans/create-and-list-plans.md b/examples/create-and-list-plans/create-and-list-plans.md new file mode 100644 index 0000000..d0166cf --- /dev/null +++ b/examples/create-and-list-plans/create-and-list-plans.md @@ -0,0 +1,27 @@ +## Create and List Subscription Plans + +This use case demonstrates how the PayPal Subscriptions API can be used to create a new subscription plan and list all available plans. It showcases the ability to define billing plans for services and retrieve plan details, which is useful for applications managing subscription-based offerings, such as SaaS platforms. + +## Prerequisites + +### 1. Set up PayPal Developer account + +Refer to the [Setup Guide](https://github.com/ballerina-platform/module-ballerinax-paypal.subscriptions#setup-guide) to obtain necessary credentials (`clientId`, `clientSecret`) and create a product to get a `productId`. + +### 2. Configuration + +Create a `Config.toml` file in the example's root directory and provide your PayPal credentials and product ID as follows: + +```toml +clientId = "" +clientSecret = "" +productId = "" +``` + +## Run the Example + +Execute the following command to run the example: + +```bash +bal run +``` \ No newline at end of file diff --git a/examples/create-and-list-plans/main.bal b/examples/create-and-list-plans/main.bal new file mode 100644 index 0000000..fd473c0 --- /dev/null +++ b/examples/create-and-list-plans/main.bal @@ -0,0 +1,69 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/io; +import ballerinax/paypal.subscriptions as paypal; + +configurable string clientId = ?; +configurable string clientSecret = ?; +configurable string productId = ?; + +final paypal:Client paypal = check new paypal:Client({auth: {clientId, clientSecret}}); + +public function main() returns error? { + // Create a subscription plan + paypal:PlanRequestPOST planPayload = { + product_id: productId, + name: "Basic Monthly Plan", + description: "Monthly subscription for premium access", + status: "ACTIVE", + billing_cycles: [ + { + frequency: { + interval_unit: "MONTH", + interval_count: 1 + }, + tenure_type: "REGULAR", + sequence: 1, + total_cycles: 12, + pricing_scheme: { + fixed_price: { + value: "10.00", + currency_code: "USD" + } + } + } + ], + payment_preferences: { + auto_bill_outstanding: true, + setup_fee: { + value: "0.00", + currency_code: "USD" + }, + setup_fee_failure_action: "CONTINUE", + payment_failure_threshold: 3 + } + }; + paypal:Plan createdPlan = check paypal->/plans.post(planPayload); + io:println("Created Plan ID: ", createdPlan.id); + + // List all plans + paypal:PlanCollection planList = check paypal->/plans(); + io:println("Available Plans:"); + foreach var plan in planList.plans ?: [] { + io:println("- Plan ID: ", plan.id, ", Name: ", plan.name); + } +} diff --git a/examples/monitor-and-manage-subscription/Ballerina.toml b/examples/monitor-and-manage-subscription/Ballerina.toml new file mode 100644 index 0000000..d3141d6 --- /dev/null +++ b/examples/monitor-and-manage-subscription/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "wso2" +name = "monitor_and_manage_subscription" +version = "0.1.0" +distribution = "2201.12.7" + +[build-options] +observabilityIncluded = true diff --git a/examples/monitor-and-manage-subscription/main.bal b/examples/monitor-and-manage-subscription/main.bal new file mode 100644 index 0000000..d66a265 --- /dev/null +++ b/examples/monitor-and-manage-subscription/main.bal @@ -0,0 +1,55 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/io; +import ballerinax/paypal.subscriptions as paypal; + +configurable string clientId = ?; +configurable string clientSecret = ?; +configurable string subscriptionId = ?; + +final paypal:Client paypal = check new ({ + auth: { + clientId, + clientSecret + } +}); + +public function main() returns error? { + // Retrieve subscription details + paypal:SubscriptionsGetQueries queries = { + fields: "id,plan_id,status" + }; + paypal:Subscription subscription = check paypal->/subscriptions/[subscriptionId].get(queries = queries); + io:println("Subscription ID: ", subscription.id, ", Status: ", subscription.status); + + // Suspend or reactivate based on status + if subscription.status == "ACTIVE" { + paypal:SubscriptionSuspendRequest suspendPayload = { + reason: "Temporary pause due to customer request" + }; + check paypal->/subscriptions/[subscriptionId]/suspend.post(suspendPayload); + io:println("Subscription suspended successfully"); + } else if subscription.status == "SUSPENDED" { + paypal:SubscriptionActivateRequest activatePayload = { + reason: "Reactivating subscription per customer request" + }; + check paypal->/subscriptions/[subscriptionId]/activate.post(activatePayload); + io:println("Subscription reactivated successfully"); + } else { + io:println("Subscription status is ", subscription.status, "; no action taken"); + } +} diff --git a/examples/monitor-and-manage-subscription/monitor-and-manage-subscription.md b/examples/monitor-and-manage-subscription/monitor-and-manage-subscription.md new file mode 100644 index 0000000..8374d6c --- /dev/null +++ b/examples/monitor-and-manage-subscription/monitor-and-manage-subscription.md @@ -0,0 +1,27 @@ +## Monitor and Manage Subscription Status + +This use case demonstrates how the PayPal Subscriptions API can be used to monitor a subscription's status and manage it by suspending or reactivating it. It is useful for applications that need to handle customer subscription status changes, such as pausing a subscription due to payment issues or reactivating it upon resolution, in scenarios like SaaS or subscription-based services. + +## Prerequisites + +### 1. Set up PayPal Developer account + +Refer to the [Setup Guide](https://github.com/ballerina-platform/module-ballerinax-paypal.subscriptions#setup-guide) to obtain necessary credentials (`clientId`, `clientSecret`) and create a subscription to get a `subscriptionId`. + +### 2. Configuration + +Create a `Config.toml` file in the example's root directory and provide your PayPal credentials and subscription ID as follows: + +```toml +clientId = "" +clientSecret = "" +subscriptionId = "" +``` + +## Run the Example + +Execute the following command to run the example: + +```bash +bal run +``` \ No newline at end of file