Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions app/models/spree_stripe/gateway.rb
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ def create_profile(payment)
payment.source.update(gateway_customer_profile_id: customer.profile_id) if payment.source.present? && customer.present?
end

def restricted_api_key?
preferred_secret_key&.start_with?('rk_')
end

def api_options
{ api_key: preferred_secret_key }
end
Expand All @@ -305,11 +309,15 @@ def send_request
private

def validate_secret_key
Stripe::Refund.list({ limit: 0 }, api_options)
Stripe::PaymentIntent.list({ limit: 1 }, api_options)
Comment thread
brozek95 marked this conversation as resolved.
rescue Stripe::AuthenticationError
errors.add(:base, 'Secret key is invalid')
rescue Stripe::PermissionError => e
errors.add(:base, 'You have provided your publishable key instead of your secret key') if e.error&.code == 'secret_key_required'
if e.error&.code == 'secret_key_required'
errors.add(:base, I18n.t('spree.stripe.errors.publishable_key_used'))
else
errors.add(:base, I18n.t('spree.stripe.errors.rak_missing_permissions'))
end
rescue Stripe::StripeError
errors.add(:base, 'Something went wrong with Stripe. Try again later.')
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="alert alert-info">
<div>
Please follow
<%= external_link_to 'how to configure Stripe payment method in Spree Commerce guide', 'https://spreecommerce.org/docs/integrations/payments/stripe', class: 'alert-link' %>.
</div>
</div>

<% if @payment_method.preferred_secret_key.present? && !@payment_method.restricted_api_key? %>
<div class="alert alert-info" role="alert">
<%= Spree.t('stripe.configuration_guide.rak_migration_tip') %>
</div>
<% end %>
5 changes: 5 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ en:
spree:
bank_transfer: Bank Transfer
stripe:
configuration_guide:
rak_migration_tip: 'Tip: You can switch to a Restricted API Key for improved security. Install the Spree Commerce app from the Stripe App Marketplace to get a scoped key with only the permissions Spree needs.'
errors:
publishable_key_used: You have provided your publishable key instead of your secret key
rak_missing_permissions: Your Restricted API Key is missing required permissions. Please reinstall the Spree Commerce app from the Stripe App Marketplace to get a correctly scoped key.
payment_intent_errors:
canceled: Payment intent canceled
processing: Payment intent processing
Expand Down
22 changes: 22 additions & 0 deletions spec/models/spree_stripe/gateway_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,28 @@
end
end

describe '#restricted_api_key?' do
subject { gateway.restricted_api_key? }

context 'when the secret key starts with rk_' do
before { gateway.preferred_secret_key = 'rk_live_abc123' }
Comment thread
brozek95 marked this conversation as resolved.

it { is_expected.to be(true) }
end

context 'when the secret key starts with sk_' do
before { gateway.preferred_secret_key = 'sk_live_abc123' }

it { is_expected.to be(false) }
end

context 'when the secret key is nil' do
before { gateway.preferred_secret_key = nil }

it { is_expected.to be(false) }
end
end

describe '#void' do
subject(:void) { gateway.void(payment_intent_id, nil, nil) }

Expand Down
22 changes: 22 additions & 0 deletions spree-commerce/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules

# testing
/coverage

# production
/.build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
install-deps.log
32 changes: 32 additions & 0 deletions spree-commerce/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Spree Commerce — Stripe App

This directory contains the Stripe App for Spree Commerce. The app generates a Restricted API Key (RAK) with scoped permissions that users paste into Spree Admin, replacing the need for full secret keys.

## Setup

Installation guide: https://docs.stripe.com/stripe-apps/plugins/rak

## Permissions

The app requests the following permissions (configured in `stripe-app.json`):

- `payment_intent_write` / `payment_intent_read` — payment processing
- `payment_method_write` / `payment_method_read` — saved payment sources
- `customer_write` / `customer_read` — Stripe customer profiles
- `charge_write` / `charge_read` — refunds and charge details
- `setup_intent_write` — saving payment methods to customer profiles
- `payment_method_domain_write` — registering domains for Apple Pay and wallet payments
- `tax_calculations_and_transactions_write` / `tax_calculations_and_transactions_read` — Stripe Tax
- `webhook_write` / `webhook_read` — managing webhook endpoints
- `connected_account_read` — multi-vendor support

## Settings Page

The settings UI is in `src/views/AppSettings.tsx`. It links to the Spree Stripe setup guide:

[Spree Stripe setup guide](https://spreecommerce.org/docs/integrations/payments/stripe)

## Preview and Publish

- Preview locally: `stripe apps start`
- Upload for review: `stripe apps upload`, then submit in Stripe Dashboard
Binary file added spree-commerce/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions spree-commerce/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* eslint-env node */
/* eslint-disable @typescript-eslint/no-var-requires */
const UIExtensionsConfig = require("@stripe/ui-extension-tools/jest.config.ui-extension");

module.exports = {
...UIExtensionsConfig,
};
28 changes: 28 additions & 0 deletions spree-commerce/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "com.spreecommerce.spree-commerce",
"version": "0.0.2",
"description": "Spree Commerce",
"license": "BSD-3-Clause",
"dependencies": {
"@stripe/ui-extension-sdk": "^9.0.0",
"stripe": "^13.4.0"
Comment thread
brozek95 marked this conversation as resolved.
},
"engines": {
"node": ">=18"
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"eslintConfig": {
"extends": [
"./node_modules/@stripe/ui-extension-tools/eslintrc.ui-extension.js"
]
},
"scripts": {
"lint": "eslint --ext ts,tsx src",
"test": "jest"
},
"resolutions": {
"@types/react": "^17.0.2"
},
"devDependencies": {
"@stripe/ui-extension-tools": "^0.0.1"
}
}
66 changes: 66 additions & 0 deletions spree-commerce/src/views/AppSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
Box,
Button,
Divider,
Icon,
SettingsView
} from "@stripe/ui-extension-sdk/ui";

const AppSettings = () => {
return (
<SettingsView>
<Box
css={{
padding: "large",
borderRadius: "medium",
width: "fit"
}}
>
<Box
css={{
font: "heading",
stack: "x",
alignY: "center",
gap: "small",
marginBottom: "small",
}}
>
<Icon name="sparkle" />
Spree Commerce
</Box>
<Box>
Install this app to generate a restricted API key for your Spree Commerce store.
Copy the key into your Spree Admin panel under Payments &gt; Stripe to complete setup.
</Box>
<Box
css={{
stack: "x",
gap: "small",
marginTop: "medium",
}}
>
<Button type="primary" target="_blank" href="https://spreecommerce.org/docs/integrations/payments/stripe">
View setup instructions
<Icon name="external" />
</Button>
<Button target="_blank" href="https://spreecommerce.org">
Learn more
<Icon name="external" />
</Button>
</Box>
</Box>
<Box css={{ marginTop: "large" }}>
<Box css={{ font: "heading", marginBottom: "small" }}>
Permissions
</Box>
<Divider />
<Box css={{ font: "body", marginTop: "medium" }}>
This app requests access to manage payments, customers, refunds,
and tax calculations on behalf of your Spree store.
</Box>
</Box>
</SettingsView>
);
};

export default AppSettings;
84 changes: 84 additions & 0 deletions spree-commerce/stripe-app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"$schema": "https://stripe.com/stripe-app.schema.json",
"id": "com.spreecommerce.spree-commerce",
"name": "Spree Commerce",
"version": "0.0.2",
"icon": "./icon.png",
"ui_extension": {
"views": [
{
"viewport": "settings",
"component": "AppSettings"
}
],
"content_security_policy": {
"purpose": ""
}
},
"extensions": null,
"permissions": [
{
"permission": "payment_intent_write",
"purpose": "Create, update, confirm, capture, and cancel payment intents for order processing"
},
{
"permission": "payment_intent_read",
"purpose": "Retrieve payment intent details and status"
},
{
"permission": "payment_method_write",
"purpose": "Attach payment methods to customers for saved payment sources"
},
{
"permission": "payment_method_read",
"purpose": "Retrieve payment method details for display and processing"
},
{
"permission": "customer_write",
"purpose": "Create and update Stripe customer profiles linked to Spree users"
},
{
"permission": "customer_read",
"purpose": "Retrieve customer details for payment processing"
},
{
"permission": "charge_write",
"purpose": "Process refunds for canceled or returned orders"
},
{
"permission": "charge_read",
"purpose": "Retrieve charge and refund details from completed payment intents"
},
{
"permission": "tax_calculations_and_transactions_write",
"purpose": "Calculate and record tax for orders using Stripe Tax"
},
{
"permission": "tax_calculations_and_transactions_read",
"purpose": "Retrieve tax calculation details"
},
{
"permission": "setup_intent_write",
"purpose": "Create setup intents for saving payment methods to customer profiles"
},
{
"permission": "payment_method_domain_write",
"purpose": "Register store domains for Apple Pay and other wallet payment methods"
},
{
"permission": "webhook_write",
"purpose": "Create webhook endpoints automatically when setting up Stripe payment method for receiving Stripe event notifications"
},
{
"permission": "webhook_read",
"purpose": "List existing webhook endpoints to avoid duplicates"
},
{
"permission": "connected_account_read",
"purpose": "Read connected account information for multi-vendor support"
}
],
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"distribution_type": "public",
"stripe_api_access_type": "restricted_api_key",
"sandbox_install_compatible": true
}
3 changes: 3 additions & 0 deletions spree-commerce/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "@stripe/ui-extension-tools/tsconfig.ui-extension"
}
1 change: 1 addition & 0 deletions spree-commerce/ui-extensions.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="@stripe/ui-extension-tools" />
Loading
Loading