Compare commits
15 Commits
BTCPayServ
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e833894ce | ||
|
|
b8e56e627c | ||
|
|
9ac08ba705 | ||
|
|
6f367a1c94 | ||
|
|
86daaf419b | ||
|
|
44f4aea5f6 | ||
|
|
ca62b7ed73 | ||
|
|
05c79fb545 | ||
|
|
ff26502a6a | ||
|
|
bcb889c3f3 | ||
|
|
b97a117096 | ||
|
|
b9a3d46db3 | ||
|
|
ff86808af4 | ||
|
|
8d359fb4ce | ||
|
|
80be645609 |
47
.github/workflows/delete-old-workflow-runs.yml
vendored
47
.github/workflows/delete-old-workflow-runs.yml
vendored
@ -1,47 +0,0 @@
|
||||
name: Delete old workflow runs
|
||||
on:
|
||||
schedule:
|
||||
# Run monthly, at 00:00 on the 1st day of month.
|
||||
- cron: '0 0 1 * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
days:
|
||||
description: 'Number of days.'
|
||||
required: true
|
||||
default: '30'
|
||||
minimum_runs:
|
||||
description: 'The minimum runs to keep for each workflow.'
|
||||
required: true
|
||||
default: '6'
|
||||
delete_workflow_pattern:
|
||||
description: 'The name or filename of the workflow. if not set then it will target all workflows.'
|
||||
required: false
|
||||
delete_workflow_by_state_pattern:
|
||||
description: 'Remove workflow by state: active, deleted, disabled_fork, disabled_inactivity, disabled_manually'
|
||||
required: true
|
||||
default: "All"
|
||||
type: choice
|
||||
options:
|
||||
- "All"
|
||||
- active
|
||||
- deleted
|
||||
- disabled_inactivity
|
||||
- disabled_manually
|
||||
dry_run:
|
||||
description: 'Only log actions, do not perform any delete operations.'
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
del_runs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Delete workflow runs
|
||||
uses: Mattraks/delete-workflow-runs@v2
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
repository: ${{ github.repository }}
|
||||
retain_days: ${{ github.event.inputs.days }}
|
||||
keep_minimum_runs: ${{ github.event.inputs.minimum_runs }}
|
||||
delete_workflow_pattern: ${{ github.event.inputs.delete_workflow_pattern }}
|
||||
delete_workflow_by_state_pattern: ${{ github.event.inputs.delete_workflow_by_state_pattern }}
|
||||
dry_run: ${{ github.event.inputs.dry_run }}
|
||||
58
.github/workflows/publish-btcpay.yml
vendored
Normal file
58
.github/workflows/publish-btcpay.yml
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
name: Create Release
|
||||
|
||||
on:
|
||||
create:
|
||||
tags:
|
||||
- 'BTCPayServer/*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up .NET 7
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '7.x' # Use .NET 7
|
||||
|
||||
- name: Run build script and capture output
|
||||
run: |
|
||||
chmod +x $GITHUB_WORKSPACE/build-btcpayplugin.sh
|
||||
ASSET_PATH=$($GITHUB_WORKSPACE/build-btcpayplugin.sh | tail -1)
|
||||
echo "ASSET_PATH=$ASSET_PATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Verify tag matches asset file name
|
||||
run: |
|
||||
TAG_NAME=${GITHUB_REF#refs/tags/BTCPayServer/}
|
||||
ASSET_BASE_NAME=$(basename $ASSET_PATH .zip)
|
||||
if [[ "$ASSET_BASE_NAME" != *"$TAG_NAME" ]]; then
|
||||
echo "Error: Tag name does not match asset file name."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Extract file name
|
||||
run: echo "ASSET_NAME=$(basename ${{ env.ASSET_PATH }})" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ${{ env.ASSET_PATH }}
|
||||
asset_name: ${{ env.ASSET_NAME }}
|
||||
asset_content_type: application/zip
|
||||
|
||||
75
.github/workflows/publish-nightly.yml
vendored
75
.github/workflows/publish-nightly.yml
vendored
@ -1,75 +0,0 @@
|
||||
name: Publish nightly dev Docker image
|
||||
|
||||
on:
|
||||
# Manual trigger
|
||||
workflow_dispatch:
|
||||
# At 06:00 AM, on Tuesday, Thursday, and Saturday
|
||||
schedule:
|
||||
- cron: "0 6 * * TUE,THU,SAT"
|
||||
|
||||
env:
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||
|
||||
jobs:
|
||||
|
||||
publish-nightly:
|
||||
name: Build, Test, Publish and Deploy nightly Docker image
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# This is used to complete the identity challenge
|
||||
# with sigstore/fulcio when running outside of PRs.
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
include-prerelease: true
|
||||
|
||||
- name: Build
|
||||
run: dotnet build Smartstore.sln -c Release
|
||||
|
||||
- name: Test
|
||||
run: dotnet test Smartstore.sln -c Release --no-restore --no-build
|
||||
|
||||
- name: Log in to the GitHub Container registry
|
||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
#- name: Log in to Docker Hub
|
||||
# uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||
# with:
|
||||
# username: ${{ secrets.DOCKER_USERNAME }}
|
||||
# password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Publish and Push for Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
dotnet publish src/Smartstore.Web/Smartstore.Web.csproj -c Release -o ./.build/release --no-restore --no-build --no-self-contained
|
||||
docker build --build-arg SOURCE=./.build/release -f NoBuild.Dockerfile -t ghcr.io/smartstore/smartstore-linux:nightly .
|
||||
docker push ghcr.io/smartstore/smartstore-linux:nightly
|
||||
#docker build --build-arg VERSION=nightly -f NoBuild.Dockerfile -t smartstore/smartstore-linux:nightly .
|
||||
#docker push smartstore/smartstore-linux:nightly
|
||||
|
||||
- name: Publish and Push for Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
dotnet publish src/Smartstore.Web/Smartstore.Web.csproj -c Release -o ./.build/release --no-restore --no-build --no-self-contained
|
||||
docker build --build-arg SOURCE=./.build/release -f Nano.Dockerfile -t ghcr.io/smartstore/smartstore-windows:nightly .
|
||||
docker push ghcr.io/smartstore/smartstore-windows:nightly
|
||||
#docker build --build-arg VERSION=nightly -f NoBuild.Dockerfile -t smartstore/smartstore-windows:nightly .
|
||||
#docker push smartstore/smartstore-windows:nightly
|
||||
70
.github/workflows/publish-release.yml
vendored
70
.github/workflows/publish-release.yml
vendored
@ -1,70 +0,0 @@
|
||||
name: Publish release Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '5.0.5.x' # << Change this for new versions.
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
publish-release:
|
||||
name: Build, Publish and Deploy stable release Docker image
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# This is used to complete the identity challenge
|
||||
# with sigstore/fulcio when running outside of PRs.
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Format branch label
|
||||
id: format-branch-label
|
||||
run: bash .github/FormatBranchLabel.sh
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x # << Change this to current .NET version.
|
||||
include-prerelease: false
|
||||
|
||||
- name: Build
|
||||
run: dotnet build Smartstore.sln -c Release
|
||||
|
||||
- name: Log in to the GitHub Container registry
|
||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Publish and Push for Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
dotnet publish src/Smartstore.Web/Smartstore.Web.csproj -c Release -o ./.build/release --no-restore --no-build --no-self-contained
|
||||
docker build --build-arg SOURCE=./.build/release -f NoBuild.Dockerfile -t ghcr.io/smartstore/smartstore-linux:latest -t ghcr.io/smartstore/smartstore-linux:${{ steps.format-branch-label.outputs.branch-label }} .
|
||||
docker push ghcr.io/smartstore/smartstore-linux:latest
|
||||
docker push ghcr.io/smartstore/smartstore-linux:${{ steps.format-branch-label.outputs.branch-label }}
|
||||
|
||||
- name: Publish and Push for Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
dotnet publish src/Smartstore.Web/Smartstore.Web.csproj -c Release -o ./.build/release --no-restore --no-build --no-self-contained
|
||||
docker build --build-arg SOURCE=./.build/release -f Nano.Dockerfile -t ghcr.io/smartstore/smartstore-windows:latest -t ghcr.io/smartstore/smartstore-windows:${{ steps.format-branch-label.outputs.branch-label }} .
|
||||
docker push ghcr.io/smartstore/smartstore-windows:latest
|
||||
docker push ghcr.io/smartstore/smartstore-windows:${{ steps.format-branch-label.outputs.branch-label }}
|
||||
18
README.md
18
README.md
@ -1,3 +1,21 @@
|
||||
**NOTE: This fork of Smartstore hosts the plugin source code for the BTCPay Server integration. You can view its README at [src/Smartstore.Modules/Smartstore.BTCPayServer/README.md](src/Smartstore.Modules/Smartstore.BTCPayServer/README.md).**
|
||||
|
||||
## Building the plugin
|
||||
Running `build-btcpayplugin.ps1` or `build-btcpayplugin.sh` will build the plugin and place the resulting zip file in `build/packages` which you can then use to install.
|
||||
|
||||
## Releasing the plugin
|
||||
A github actions workflow is configured to automatically build and release the plugin when a new tag is pushed to the repository.
|
||||
The workflow will build the plugin and upload it to the [releases page](https://github.com/btcpayserver/Smartstore.BTCPayServer/releases/)
|
||||
The tag name must be in the format `BTCPayServer/{version}`, where `{version}` is the version of the plugin as defined in `src\Smartstore.Modules\Smartstore.BTCPayServer\module.json`.
|
||||
|
||||
## Testing without debugging
|
||||
Simply run `docker compose up` in this directory and the latest version of SmartStore will be run on `http://localhost`. Just use the SQLite db option and you're good to go.
|
||||
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.smartstore.com" target="_blank" rel="noopener noreferrer">
|
||||
<img src="assets/smartstore-icon-whitebg.png" alt="Smartstore" width="120">
|
||||
|
||||
@ -83,6 +83,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Smartstore.Data.PostgreSql"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Smartstore.Data.Sqlite", "src\Smartstore.Data\Smartstore.Data.Sqlite\Smartstore.Data.Sqlite.csproj", "{32683DB7-C20D-4ABD-BE90-569168B2A68F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Smartstore.BTCPayServer", "src\Smartstore.Modules\Smartstore.BTCPayServer\Smartstore.BTCPayServer.csproj", "{5580D90E-3E5F-4D4A-8DFC-DE7969FBB9C3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Smartstore.Packager", "tools\Smartstore.Packager\Smartstore.Packager.csproj", "{B59EAEF6-5E27-42C9-9BF3-0201BE82147B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Smartstore.PackagerCLI", "tools\Smartstore.PackagerCLI\Smartstore.PackagerCLI.csproj", "{8AB115E0-CB09-4A2B-B3D5-3BB42A321957}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -270,6 +276,24 @@ Global
|
||||
{32683DB7-C20D-4ABD-BE90-569168B2A68F}.DebugNoRazorCompile|Any CPU.Build.0 = DebugNoRazorCompile|Any CPU
|
||||
{32683DB7-C20D-4ABD-BE90-569168B2A68F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{32683DB7-C20D-4ABD-BE90-569168B2A68F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5580D90E-3E5F-4D4A-8DFC-DE7969FBB9C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5580D90E-3E5F-4D4A-8DFC-DE7969FBB9C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5580D90E-3E5F-4D4A-8DFC-DE7969FBB9C3}.DebugNoRazorCompile|Any CPU.ActiveCfg = DebugNoRazorCompile|Any CPU
|
||||
{5580D90E-3E5F-4D4A-8DFC-DE7969FBB9C3}.DebugNoRazorCompile|Any CPU.Build.0 = DebugNoRazorCompile|Any CPU
|
||||
{5580D90E-3E5F-4D4A-8DFC-DE7969FBB9C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5580D90E-3E5F-4D4A-8DFC-DE7969FBB9C3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B59EAEF6-5E27-42C9-9BF3-0201BE82147B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B59EAEF6-5E27-42C9-9BF3-0201BE82147B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B59EAEF6-5E27-42C9-9BF3-0201BE82147B}.DebugNoRazorCompile|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B59EAEF6-5E27-42C9-9BF3-0201BE82147B}.DebugNoRazorCompile|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B59EAEF6-5E27-42C9-9BF3-0201BE82147B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B59EAEF6-5E27-42C9-9BF3-0201BE82147B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8AB115E0-CB09-4A2B-B3D5-3BB42A321957}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8AB115E0-CB09-4A2B-B3D5-3BB42A321957}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8AB115E0-CB09-4A2B-B3D5-3BB42A321957}.DebugNoRazorCompile|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8AB115E0-CB09-4A2B-B3D5-3BB42A321957}.DebugNoRazorCompile|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8AB115E0-CB09-4A2B-B3D5-3BB42A321957}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8AB115E0-CB09-4A2B-B3D5-3BB42A321957}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -301,6 +325,7 @@ Global
|
||||
{0C26B088-A8F6-42E8-AB8D-D9D297FDD72C} = {E5C1AA43-6B44-4EFA-8DE9-6070EE007CE3}
|
||||
{1A016177-D0EA-49AD-9BD5-BAD99D9D08F9} = {82A38DDA-C944-4C15-AF43-86732F8ED15A}
|
||||
{32683DB7-C20D-4ABD-BE90-569168B2A68F} = {82A38DDA-C944-4C15-AF43-86732F8ED15A}
|
||||
{5580D90E-3E5F-4D4A-8DFC-DE7969FBB9C3} = {E5C1AA43-6B44-4EFA-8DE9-6070EE007CE3}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A0A0B995-5D0B-458E-BCC0-1071B76949D1}
|
||||
|
||||
5
build-btcpayplugin.ps1
Normal file
5
build-btcpayplugin.ps1
Normal file
@ -0,0 +1,5 @@
|
||||
$StartPath = Split-Path -Parent $PSCommandPath
|
||||
echo "$StartPath"
|
||||
dotnet build $StartPath/src/Smartstore.Modules/Smartstore.BTCPayServer/Smartstore.BTCPayServer.csproj -c Release
|
||||
echo "$StartPath/tools/Smartstore.PackagerCLI/Smartstore.PackagerCLI.csproj"
|
||||
dotnet run --project $StartPath/tools/Smartstore.PackagerCLI/Smartstore.PackagerCLI.csproj "$StartPath/src/Smartstore.Web/Modules/Smartstore.BTCPayServer" "$StartPath/build/packages"
|
||||
16
build-btcpayplugin.sh
Executable file
16
build-btcpayplugin.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the absolute directory path of the currently running script
|
||||
StartPath=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
|
||||
|
||||
# Print the StartPath
|
||||
echo "$StartPath"
|
||||
|
||||
# Build the project
|
||||
dotnet build "$StartPath/src/Smartstore.Modules/Smartstore.BTCPayServer/Smartstore.BTCPayServer.csproj" -c Release
|
||||
|
||||
# Print the path to the PackagerCLI project
|
||||
echo "$StartPath/tools/Smartstore.PackagerCLI/Smartstore.PackagerCLI.csproj"
|
||||
|
||||
# Run the PackagerCLI project
|
||||
dotnet run --project "$StartPath/tools/Smartstore.PackagerCLI/Smartstore.PackagerCLI.csproj" "$StartPath/src/Smartstore.Web/Modules/Smartstore.BTCPayServer" "$StartPath/build/packages"
|
||||
@ -2,28 +2,7 @@ version: "3.4"
|
||||
services:
|
||||
web:
|
||||
image: ghcr.io/smartstore/smartstore-linux
|
||||
container_name: web
|
||||
container_name: smartstore_btcpay
|
||||
ports:
|
||||
- "80:80"
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
volumes:
|
||||
- "D:/mount/smtenants/mysql:/app/App_Data/Tenants"
|
||||
db:
|
||||
image: mysql
|
||||
container_name: mysql
|
||||
environment:
|
||||
#MYSQL_DATABASE: smartstore
|
||||
#MYSQL_USER: "root"
|
||||
MYSQL_PASSWORD: "Smartstore2022!"
|
||||
MYSQL_ROOT_PASSWORD: "Smartstore2022!"
|
||||
ports:
|
||||
- '3307:3306'
|
||||
expose:
|
||||
- '3306'
|
||||
volumes:
|
||||
- mysql:/var/lib/mysql
|
||||
|
||||
volumes:
|
||||
mysql:
|
||||
@ -10,6 +10,7 @@ using Smartstore.Core;
|
||||
using System.Web;
|
||||
using BTCPayServer.Client;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Smartstore.BTCPayServer.Configuration;
|
||||
using Smartstore.BTCPayServer.Services;
|
||||
@ -18,26 +19,26 @@ using Smartstore.Core.Checkout.Payment;
|
||||
namespace Smartstore.BTCPayServer.Controllers
|
||||
{
|
||||
[Area("Admin")]
|
||||
[Route("[area]/btcpay/{action=index}/{id?}")]
|
||||
[Route("[area]/btcpayserver/{action=index}/{id?}")]
|
||||
public class BtcPayAdminController : ModuleController
|
||||
{
|
||||
|
||||
private readonly ICommonServices _services;
|
||||
private readonly BtcPayService _btcPayService;
|
||||
private readonly LinkGenerator _linkGenerator;
|
||||
private readonly IProviderManager _providerManager;
|
||||
private readonly ICurrencyService _currencyService;
|
||||
private readonly PaymentSettings _paymentSettings;
|
||||
|
||||
public BtcPayAdminController(
|
||||
BtcPayService btcPayService,
|
||||
ILogger<BtcPayAdminController> logger,
|
||||
ICommonServices services,
|
||||
IProviderManager providerManager,
|
||||
ICurrencyService currencyService,
|
||||
|
||||
LinkGenerator linkGenerator,
|
||||
ICommonServices services,
|
||||
IProviderManager providerManager,
|
||||
ICurrencyService currencyService,
|
||||
PaymentSettings settings)
|
||||
{
|
||||
_btcPayService = btcPayService;
|
||||
_linkGenerator = linkGenerator;
|
||||
_providerManager = providerManager;
|
||||
_currencyService = currencyService;
|
||||
_paymentSettings = settings;
|
||||
@ -49,11 +50,15 @@ namespace Smartstore.BTCPayServer.Controllers
|
||||
{
|
||||
var model = MiniMapper.Map<BtcPaySettings, ConfigurationModel>(settings);
|
||||
var myStore = _services.StoreContext.CurrentStore;
|
||||
|
||||
|
||||
ViewBag.Provider = _providerManager.GetProvider("Smartstore.BTCPayServer").Metadata;
|
||||
ViewBag.StoreCurrencyCode = _currencyService.PrimaryCurrency.CurrencyCode ?? "EUR";
|
||||
ViewBag.UrlWebHook = myStore.Url + "BtcPayHook/Process";
|
||||
|
||||
ViewBag.UrlWebHook = new Uri(new Uri(myStore.Url),
|
||||
_linkGenerator.GetPathByAction("Process", "BtcPayHook"));
|
||||
|
||||
|
||||
var sViewMsg = HttpContext.Session.GetString("ViewMsg");
|
||||
if (!string.IsNullOrEmpty(sViewMsg))
|
||||
{
|
||||
@ -68,16 +73,6 @@ namespace Smartstore.BTCPayServer.Controllers
|
||||
HttpContext.Session.SetString("ViewMsgError", "");
|
||||
}
|
||||
|
||||
var sUrl = "";
|
||||
if (!string.IsNullOrEmpty(model.BtcPayUrl))
|
||||
{
|
||||
|
||||
sUrl = model.BtcPayUrl + (model.BtcPayUrl.EndsWith("/") ? "" : "/");
|
||||
sUrl += $"api-keys/authorize?applicationName={myStore.Name.Replace(" ", "")}&applicationIdentifier=SmartStore{myStore.Id}&selectiveStores=true"
|
||||
+ $"&redirect={myStore.Url}admin/btcpay/getautomaticapikeyconfig&permissions=btcpay.store.canmodifystoresettings";
|
||||
}
|
||||
ViewBag.UrlBtcApiKey = sUrl;
|
||||
ViewBag.UrlCreateWebHook = myStore.Url + "admin/btcpay/createwebhook/";
|
||||
return View(model);
|
||||
}
|
||||
|
||||
@ -90,19 +85,26 @@ namespace Smartstore.BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
var myStore = _services.StoreContext.CurrentStore;
|
||||
var adminUrl = myStore.Url + "admin/btcpay/getautomaticapikeyconfig";
|
||||
var adminUrlParams = new Dictionary<string, string>();
|
||||
adminUrlParams.Add("ssid", myStore.Id.ToString());
|
||||
adminUrlParams.Add("btcpayuri", btcpayUri.ToString());
|
||||
adminUrl += QueryString.Create(adminUrlParams);
|
||||
|
||||
var uri = BTCPayServerClient.GenerateAuthorizeUri(btcpayUri, new[] {"btcpay.store.canmodifystoresettings"},
|
||||
true, true, ($"SmartStore{myStore.Id}", new Uri(adminUrl)));
|
||||
var adminUrl = new Uri(new Uri(myStore.Url),
|
||||
_linkGenerator.GetPathByAction(HttpContext,"GetAutomaticApiKeyConfig", "BtcPayAdmin",
|
||||
new {ssid = myStore.Id, btcpayuri = btcpayUri}));
|
||||
var uri = BTCPayServerClient.GenerateAuthorizeUri(btcpayUri,
|
||||
new[]
|
||||
{
|
||||
Policies.CanCreateInvoice, // create invoices for payment
|
||||
Policies.CanViewInvoices, // fetch created invoices to check status
|
||||
Policies.CanModifyInvoices, // able to mark an invoice invalid in case merchant wants to void the order
|
||||
Policies.CanModifyStoreWebhooks, // able to create the webhook required automatically
|
||||
Policies.CanViewStoreSettings, // able to fetch rates
|
||||
Policies.CanCreateNonApprovedPullPayments // able to create refunds
|
||||
},
|
||||
true, true, ($"SmartStore{myStore.Id}", adminUrl));
|
||||
return uri + $"&applicationName={HttpUtility.UrlEncode(myStore.Name)}";
|
||||
}
|
||||
|
||||
[HttpPost, SaveSetting, AuthorizeAdmin]
|
||||
public async Task<IActionResult> Configure(ConfigurationModel model, BtcPaySettings settings , string command= null)
|
||||
public async Task<IActionResult> Configure(ConfigurationModel model, BtcPaySettings settings,
|
||||
string command = null)
|
||||
{
|
||||
if (command == "delete")
|
||||
{
|
||||
@ -112,7 +114,7 @@ namespace Smartstore.BTCPayServer.Controllers
|
||||
settings.BtcPayUrl = "";
|
||||
ModelState.Clear();
|
||||
_paymentSettings.ActivePaymentMethodSystemNames.Remove("Smartstore.BTCPayServer");
|
||||
|
||||
|
||||
HttpContext.Session.SetString("ViewMsg", "Settings cleared and payment method deactivated");
|
||||
return Configure(settings);
|
||||
}
|
||||
@ -120,15 +122,14 @@ namespace Smartstore.BTCPayServer.Controllers
|
||||
if (command == "activate" && model.IsConfigured())
|
||||
{
|
||||
_paymentSettings.ActivePaymentMethodSystemNames.Add("Smartstore.BTCPayServer");
|
||||
|
||||
|
||||
await _services.SettingFactory.SaveSettingsAsync(_paymentSettings);
|
||||
|
||||
|
||||
HttpContext.Session.SetString("ViewMsg", "Payment method activated");
|
||||
}
|
||||
|
||||
if (command == "getautomaticapikeyconfig")
|
||||
{
|
||||
|
||||
MiniMapper.Map(model, settings);
|
||||
string? result = GetRedirectUri(settings);
|
||||
if (result != null)
|
||||
@ -160,75 +161,59 @@ namespace Smartstore.BTCPayServer.Controllers
|
||||
public async Task<IActionResult> GetAutomaticApiKeyConfig()
|
||||
{
|
||||
Request.Query.TryGetValue("ssid", out var ssidx);
|
||||
var ssid = int.Parse(ssidx.FirstOrDefault() ?? _services.StoreContext.CurrentStore.Id.ToString());
|
||||
var settings = await _services.SettingFactory.LoadSettingsAsync<BtcPaySettings>(ssid);
|
||||
try
|
||||
var ssid = int.Parse(ssidx.FirstOrDefault() ?? _services.StoreContext.CurrentStore.Id.ToString());
|
||||
if (ssid != _services.StoreContext.CurrentStore.Id)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var settings = await _services.SettingFactory.LoadSettingsAsync<BtcPaySettings>(ssid);
|
||||
try
|
||||
{
|
||||
|
||||
Request.Form.TryGetValue("apiKey", out var apiKey);
|
||||
Request.Form.TryGetValue("permissions[]", out var permissions);
|
||||
Permission.TryParse(permissions.FirstOrDefault(), out var permission);
|
||||
if (Request.Query.TryGetValue("btcpayuri", out var btcpayUris) && btcpayUris.FirstOrDefault() is { } stringbtcpayUri)
|
||||
if (Request.Query.TryGetValue("btcpayuri", out var btcpayUris) &&
|
||||
btcpayUris.FirstOrDefault() is { } stringbtcpayUri)
|
||||
{
|
||||
settings.BtcPayUrl = stringbtcpayUri;
|
||||
}
|
||||
|
||||
settings.ApiKey = apiKey;
|
||||
settings.BtcPayStoreID = permission.Scope;
|
||||
try
|
||||
{
|
||||
if (permission.Scope is null)
|
||||
{
|
||||
settings.BtcPayStoreID = await _btcPayService.GetStoreId(settings);
|
||||
settings.BtcPayStoreID = await _btcPayService.GetStoreId(settings);
|
||||
}
|
||||
|
||||
if(string.IsNullOrEmpty(settings.WebHookSecret))
|
||||
if (string.IsNullOrEmpty(settings.WebHookSecret))
|
||||
{
|
||||
settings.WebHookSecret = await _btcPayService.CreateWebHook(settings, _services.StoreContext.CurrentStore.Url + "BtcPayHook/Process");
|
||||
var webhookUrl = new Uri(new Uri(_services.StoreContext.CurrentStore.Url),
|
||||
_linkGenerator.GetPathByAction("Process", "BtcPayHook"));
|
||||
settings.WebHookSecret = await _btcPayService.CreateWebHook(settings, webhookUrl.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
|
||||
_paymentSettings.ActivePaymentMethodSystemNames.Add("Smartstore.BTCPayServer");
|
||||
|
||||
|
||||
await _services.SettingFactory.SaveSettingsAsync(_paymentSettings);
|
||||
await _services.SettingFactory.SaveSettingsAsync(_paymentSettings);
|
||||
|
||||
HttpContext.Session.SetString("ViewMsg", "API Key and Store ID set with success. Don't forget to click on <b>Save</b> button to update data !");
|
||||
await _services.SettingFactory.SaveSettingsAsync(settings);
|
||||
|
||||
HttpContext.Session.SetString("ViewMsg",
|
||||
"Settings automatically configured and payment method activated.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HttpContext.Session.SetString("ViewMsgError", "Error during API Key creation !");
|
||||
HttpContext.Session.SetString("ViewMsgError", "Error during automatic configuration");
|
||||
Logger.Error(ex.Message);
|
||||
}
|
||||
return RedirectToAction(nameof(Configure), settings);
|
||||
|
||||
return RedirectToAction(nameof(Configure));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> CreateWebHook()
|
||||
{
|
||||
var myStore = _services.StoreContext.CurrentStore;
|
||||
var settings = await _services.SettingFactory.LoadSettingsAsync<BtcPaySettings>(myStore.Id);
|
||||
|
||||
if (! (string.IsNullOrEmpty(settings.BtcPayStoreID)
|
||||
|| string.IsNullOrEmpty(settings.BtcPayUrl)
|
||||
|| string.IsNullOrEmpty(settings.ApiKey)))
|
||||
{
|
||||
try
|
||||
{
|
||||
settings.WebHookSecret = await _btcPayService.CreateWebHook(settings, myStore.Url + "BtcPayHook/Process");
|
||||
HttpContext.Session.SetString("ViewMsg", "WebHook created successfully. Don't forget to click on <b>Save</b> button to update data !");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HttpContext.Session.SetString("ViewMsgError", "Error during WebHook creation! Make sure your API Key and Store ID are correct.");
|
||||
Logger.Error(ex.Message);
|
||||
}
|
||||
}
|
||||
return RedirectToAction(nameof(Configure), settings);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -9,35 +9,31 @@ using Smartstore.BTCPayServer.Configuration;
|
||||
using Smartstore.BTCPayServer.Providers;
|
||||
using Smartstore.BTCPayServer.Services;
|
||||
using Smartstore.Core;
|
||||
using Smartstore.Core.Checkout.Orders;
|
||||
using Smartstore.Core.Checkout.Payment;
|
||||
using Smartstore.Core.Data;
|
||||
using Smartstore.Web.Controllers;
|
||||
|
||||
namespace Smartstore.BTCPayServer.Controllers
|
||||
{
|
||||
|
||||
public class BtcPayHookController : PublicController
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly BtcPayService _btcPayService;
|
||||
private readonly SmartDbContext _db;
|
||||
private readonly BtcPaySettings _settings;
|
||||
private readonly ICommonServices _services;
|
||||
|
||||
public BtcPayHookController(
|
||||
BtcPaySettings settings,
|
||||
SmartDbContext db,
|
||||
ILogger logger,
|
||||
ICommonServices services,
|
||||
BtcPayService btcPayService)
|
||||
{
|
||||
_logger = logger;
|
||||
_btcPayService = btcPayService;
|
||||
_db = db;
|
||||
_settings = settings;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost][AllowAnonymous]
|
||||
public async Task<IActionResult> Process([FromHeader(Name = "BTCPAY-SIG")] string btcPaySig)
|
||||
{
|
||||
try
|
||||
@ -45,7 +41,7 @@ namespace Smartstore.BTCPayServer.Controllers
|
||||
string jsonStr = await new StreamReader(Request.Body).ReadToEndAsync();
|
||||
var webhookEvent = JsonConvert.DeserializeObject<WebhookInvoiceEvent>(jsonStr);
|
||||
var signature = btcPaySig.Split('=')[1];
|
||||
if(webhookEvent?.InvoiceId?.StartsWith("__test__") is true)
|
||||
if(webhookEvent is null || webhookEvent?.InvoiceId?.StartsWith("__test__") is true || webhookEvent?.Type == WebhookEventType.InvoiceCreated)
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
@ -55,26 +51,24 @@ namespace Smartstore.BTCPayServer.Controllers
|
||||
Logger.Error("Missing fields in request");
|
||||
return StatusCode(StatusCodes.Status422UnprocessableEntity);
|
||||
}
|
||||
if (_settings.WebHookSecret is not null && !BtcPayService.CheckSecretKey(_settings.WebHookSecret, jsonStr, signature))
|
||||
{
|
||||
Logger.Error("Bad secret key");
|
||||
return StatusCode(StatusCodes.Status400BadRequest);
|
||||
}
|
||||
var invoice = await _btcPayService.GetInvoice(_settings, webhookEvent.InvoiceId);
|
||||
if( invoice is null)
|
||||
{
|
||||
Logger.Error("Invoice not found");
|
||||
return StatusCode(StatusCodes.Status422UnprocessableEntity);
|
||||
}
|
||||
|
||||
var order = await _db.Orders.FirstOrDefaultAsync(x =>
|
||||
x.PaymentMethodSystemName == BTCPayPaymentProvider.SystemName &&
|
||||
x.OrderGuid == new Guid(orderId));
|
||||
if (order == null)
|
||||
|
||||
if (order is null)
|
||||
{
|
||||
Logger.Error("Order not found");
|
||||
return StatusCode(StatusCodes.Status422UnprocessableEntity);
|
||||
}
|
||||
var settings = await _services.SettingFactory.LoadSettingsAsync<BtcPaySettings>(order.StoreId);
|
||||
|
||||
if (settings.WebHookSecret is not null && !BtcPayService.CheckSecretKey(settings.WebHookSecret, jsonStr, signature))
|
||||
{
|
||||
Logger.Error("Bad secret key");
|
||||
return StatusCode(StatusCodes.Status400BadRequest);
|
||||
}
|
||||
var invoice = await _btcPayService.GetInvoice(settings, webhookEvent.InvoiceId);
|
||||
|
||||
if (await _btcPayService.UpdateOrderWithInvoice(order, invoice, webhookEvent))
|
||||
{
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Smartstore.Core.Data;
|
||||
using Smartstore.Web.Controllers;
|
||||
|
||||
namespace Smartstore.BTCPayServer.Controllers
|
||||
{
|
||||
[Route("btcpayserver/order")]
|
||||
public class BtcPayOrderController : PublicController
|
||||
{
|
||||
private readonly SmartDbContext _db;
|
||||
|
||||
public BtcPayOrderController(
|
||||
SmartDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> Index(Guid id)
|
||||
{
|
||||
var orderId = (await _db.Orders.SingleOrDefaultAsync(order => order.OrderGuid == id))?.Id;
|
||||
if (orderId is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return RedirectToAction("Details", "Order", new {id = orderId});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Nisaba
|
||||
Copyright (c) 2023 BTCPay Server
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@ -1,34 +1,32 @@
|
||||
<Language Name="Deutsch" IsDefault="false" IsRightToLeft="false">
|
||||
<LocaleResource Name="Plugins.FriendlyName.SmartStore.BTCPayServer" AppendRootKey="false">
|
||||
<Value>Bezahlen Sie mit Bitcoin</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="Plugins.Description.SmartStore.BTCPayServer" AppendRootKey="false">
|
||||
<Value>Akzeptieren Sie Bitcoin in Ihrem SmartStore-Shop mit BTCPay Server</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="Plugins.FriendlyName.SmartStore.BTCPayServer" AppendRootKey="false">
|
||||
<Value>Bezahlen Sie mit Bitcoin</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="Plugins.Description.SmartStore.BTCPayServer" AppendRootKey="false">
|
||||
<Value>Akzeptieren Sie Bitcoin in Ihrem SmartStore-Shop mit BTCPay Server</Value>
|
||||
</LocaleResource>
|
||||
|
||||
<!-- (Provider name) will be displayed in frontend on payment selection page and in backend on payment configuration list -->
|
||||
<LocaleResource Name="Plugins.FriendlyName.Payments.BTCPayServer" AppendRootKey="false">
|
||||
<Value>BTCPay Server</Value>
|
||||
</LocaleResource>
|
||||
<!-- (Provider description) will be displayed in frontend on payment selection page and in backend on payment configuration list -->
|
||||
<LocaleResource Name="Plugins.Description.Payments.BTCPayServer" AppendRootKey="false">
|
||||
<Value>Akzeptieren Sie Bitcoin in Ihrem SmartStore-Shop mit BtcPay Server.</Value>
|
||||
</LocaleResource>
|
||||
<!-- (Provider name) will be displayed in frontend on payment selection page and in backend on payment configuration list -->
|
||||
<LocaleResource Name="Plugins.FriendlyName.Payments.BTCPayServer" AppendRootKey="false">
|
||||
<Value>BTCPay Server</Value>
|
||||
</LocaleResource>
|
||||
<!-- (Provider description) will be displayed in frontend on payment selection page and in backend on payment configuration list -->
|
||||
<LocaleResource Name="Plugins.Description.Payments.BTCPayServer" AppendRootKey="false">
|
||||
<Value>Akzeptieren Sie Bitcoin in Ihrem SmartStore-Shop mit BtcPay Server.</Value>
|
||||
</LocaleResource>
|
||||
|
||||
<LocaleResource Name="Plugins.SmartStore.BTCPayServer" AppendRootKey="false">
|
||||
<Children>
|
||||
<LocaleResource Name="AdminInstruction">
|
||||
<Value>
|
||||
<![CDATA[
|
||||
<LocaleResource Name="Plugins.SmartStore.BTCPayServer" AppendRootKey="false">
|
||||
<Children>
|
||||
<LocaleResource Name="AdminInstruction">
|
||||
<Value>
|
||||
<![CDATA[
|
||||
<div class="mb-1"><a href="https://github.com/Nisaba/btcpay-smartstore-plugin" target="_blank">BTCPay Plugin für SmartStore</a></div>
|
||||
<div class="mb-1">Die Konfiguration des Plugins kann automatisch oder manuell erfolgen.</div>
|
||||
<div class="mb-1"><br/><b>Automatische Konfiguration:</b></div>
|
||||
<ul>
|
||||
<li>Geben Sie den "BTCPay Url"-Parameter ein und speichern Sie ihn.</li>
|
||||
<li>Klicken Sie auf den Link "API-Key automatisch erstellen", um zur Seite zur Erstellung des Schlüssels auf Ihrem BTCPay-Server weitergeleitet zu werden.</li>
|
||||
<li>Die Parameter "API-Key" und "BTCPay Store ID" werden automatisch ausgefüllt. Speichern Sie sie.</li>
|
||||
<li>Klicken Sie auf den Link "WebHook automatisch erstellen".</li>
|
||||
<li>Das Feld "WebHook Secret" wird automatisch ausgefüllt. Speichern Sie es.</li>
|
||||
<li>Geben Sie den "BTCPay Url"-Parameter ein und speichern .</li>
|
||||
<li>Klicken Sie auf "Configure automatically", um zur Seite zur Erstellung des Schlüssels auf Ihrem BTCPay-Server weitergeleitet zu werden.</li>
|
||||
<li>Die Parameter "API-Key", "BTCPay Store ID" und "WebHook Secret" werden automatisch ausgefüllt. Speichern Sie sie.</li>
|
||||
</ul>
|
||||
<div class="mb-1"><br/><b>Manuelle Konfiguration:</b></div>
|
||||
<ul>
|
||||
@ -41,53 +39,53 @@
|
||||
<li>Um den BTCPay WebHook zu erstellen, <a href="https://docs.btcpayserver.org/VirtueMart/#23-create-a-webhook-on-btcpay-server" target="_blank">lesen Sie dies</a>. (verwenden Sie den standardmäßig von BTCPay generierten Geheimcode)</li>
|
||||
</ul>
|
||||
]]>
|
||||
</Value>
|
||||
</LocaleResource>
|
||||
</Value>
|
||||
</LocaleResource>
|
||||
|
||||
|
||||
<LocaleResource Name="WebHookNote">
|
||||
<Value>
|
||||
<![CDATA[
|
||||
<LocaleResource Name="WebHookNote">
|
||||
<Value>
|
||||
<![CDATA[
|
||||
Hinweis: Beim Testen des Webhooks von BTCPay sollte ein HTTP 422-Fehler angezeigt werden.<br/>
|
||||
Dies liegt daran, dass BTCPay leere Daten sendet, während das SmartStore-Plugin echte Daten erwartet.<br/>
|
||||
Dieser Fehler weist daher darauf hin, dass der Webhook tatsächlich über BTCPay zugänglich ist.<br/>
|
||||
Bei einer echten Transaktion können wir daher von einer korrekten Abwicklung ausgehen.
|
||||
]]>
|
||||
</Value>
|
||||
</LocaleResource>
|
||||
</Value>
|
||||
</LocaleResource>
|
||||
|
||||
|
||||
<LocaleResource Name="WebHookInfo">
|
||||
<LocaleResource Name="WebHookInfo">
|
||||
<Value>Hier ist die URL, die für die WebHook-Erstellung in BTCPay festgelegt werden muss: </Value>
|
||||
</LocaleResource>
|
||||
|
||||
|
||||
<LocaleResource Name="BtcPayUrl">
|
||||
<Value>BTCPay-URL</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="BtcPayUrl.Hint">
|
||||
<Value>Die URL Ihrer BTCPay-Instanz</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="ApiKey">
|
||||
<Value>API-Schlüssel</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="ApiKey.Hint">
|
||||
<Value>Der in Ihrer BTCPay-Instanz generierte API-Schlüsselwert</Value>
|
||||
</LocaleResource>
|
||||
<Value>BTCPay-URL</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="BtcPayUrl.Hint">
|
||||
<Value>Die URL Ihrer BTCPay-Instanz</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="ApiKey">
|
||||
<Value>API-Schlüssel</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="ApiKey.Hint">
|
||||
<Value>Der in Ihrer BTCPay-Instanz generierte API-Schlüsselwert</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="CreateApiKey">
|
||||
<Value>API-Schlüssel automatisch erstellen</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="BtcPayStoreID">
|
||||
<Value>BTCPay Store-ID</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="BtcPayStoreID.Hint">
|
||||
<Value>die BTCPay Store ID</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="WebHookSecret">
|
||||
<Value>WebHook-Geheimnis</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="WebHookSecret.Hint">
|
||||
<Value>Der in Ihrer BTCPay-Instanz generierte WebHook-Secret-Wert</Value>
|
||||
</LocaleResource>
|
||||
<Value>BTCPay Store-ID</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="BtcPayStoreID.Hint">
|
||||
<Value>die BTCPay Store ID</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="WebHookSecret">
|
||||
<Value>WebHook-Geheimnis</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="WebHookSecret.Hint">
|
||||
<Value>Der in Ihrer BTCPay-Instanz generierte WebHook-Secret-Wert</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="CreateWebhook">
|
||||
<Value>Webhook automatisch erstellen</Value>
|
||||
</LocaleResource>
|
||||
@ -95,17 +93,17 @@ Bei einer echten Transaktion können wir daher von einer korrekten Abwicklung au
|
||||
<Value>WebHook-Url</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="NoteRefund">
|
||||
<Value>Es wurde eine Rückerstattung vorgenommen. Bitte folgen Sie diesem Link:</Value>
|
||||
</LocaleResource>
|
||||
<Value>Es wurde eine Rückerstattung vorgenommen. Bitte folgen Sie diesem Link:</Value>
|
||||
</LocaleResource>
|
||||
|
||||
<LocaleResource Name="PaymentInfo">
|
||||
<Value>Nach Abschluss Ihrer Bestellung werden Sie an unseren BTCPay Server weitergeleitet, wo Sie die Zahlung für Ihre Bestellung tätigen können..</Value>
|
||||
</LocaleResource>
|
||||
<LocaleResource Name="PaymentInfo">
|
||||
<Value>Nach Abschluss Ihrer Bestellung werden Sie an unseren BTCPay Server weitergeleitet, wo Sie die Zahlung für Ihre Bestellung tätigen können..</Value>
|
||||
</LocaleResource>
|
||||
|
||||
<LocaleResource Name="PaymentError">
|
||||
<Value>Fehler bei der Verarbeitung der Zahlung. Bitte versuchen Sie es erneut und kontaktieren Sie uns, wenn das Problem weiterhin besteht.</Value>
|
||||
</LocaleResource>
|
||||
|
||||
</Children>
|
||||
</LocaleResource>
|
||||
</Children>
|
||||
</LocaleResource>
|
||||
</Language>
|
||||
@ -60,9 +60,9 @@
|
||||
<div class="mb-1">The plugin configuration can be done automatically or manually.</div>
|
||||
<div class="mb-1"><br/><b>Automatic Configuration:</b></div>
|
||||
<ul>
|
||||
<li>Enter the "BTCPay Url" parameter and save.</li>
|
||||
<li>Click on the "Create API Key Automatically" link to be redirected to the key creation page on your BTCPay server.</li>
|
||||
<li>The "API Key" and "BTCPay Store ID" parameters will be automatically filled. Save.</li>
|
||||
<li>Enter the "BTCPay Url" parameter.</li>
|
||||
<li>Click on the "Configure automatically" button to be redirected to the key creation page on your BTCPay server.</li>
|
||||
<li>The "API Key", "BTCPay Store ID" and "WebHook Secret" parameters will be automatically filled. Save.</li>
|
||||
</ul>
|
||||
<div class="mb-1"><br/><b>Manual Configuration:</b></div>
|
||||
<ul>
|
||||
@ -96,7 +96,7 @@ With a real transaction, you can therefore expect correct operation.
|
||||
<LocaleResource Name="PaymentInfo">
|
||||
<Value>After completing the order you will be redirected to the merchant BTCPay instance, where you can make the Bitcoin payment for your order.</Value>
|
||||
</LocaleResource>
|
||||
|
||||
|
||||
<LocaleResource Name="PaymentError">
|
||||
<Value>Error processing the payment. Please try again and contact us if the problem persists.</Value>
|
||||
</LocaleResource>
|
||||
|
||||
@ -24,11 +24,9 @@
|
||||
<div class="mb-1">La configuration du plugin peut se faire de manière automatique ou manuelle.</div>
|
||||
<div class="mb-1"><br/><b>Configuration automatique :</b></div>
|
||||
<ul>
|
||||
<li>Renseignez le paramètre "BTCPay Url" et sauvegardez</li>
|
||||
<li>Cliquez sur le lien "Créer la clé API automatiquement" pour être redirigé vers la page de création de la clé sur votre serveur BTCPay Server</li>
|
||||
<li>Les paramètres "Clé API" et "BTCPay Store ID" sont alors automatiquement renseignés. Sauvegardez</li>
|
||||
<li>Cliquez sur le lien "Créer le WebHook automatiquement"</li>
|
||||
<li>Le champ "WebHook Secret" est alors automatiquement renseigné. Sauvegardez</li>
|
||||
<li>Renseignez le paramètre "BTCPay Url"</li>
|
||||
<li>Cliquez sur le bouton "Configure automatically" pour être redirigé vers la page de création de la clé sur votre serveur BTCPay Server</li>
|
||||
<li>Les paramètres "Clé API", "BTCPay Store ID" et "WebHook Secret" sont alors automatiquement renseignés. Sauvegardez</li>
|
||||
</ul>
|
||||
<div class="mb-1"><br/><b>Configuration manuelle :</b></div>
|
||||
<ul>
|
||||
@ -55,7 +53,7 @@ C'est parce que BTCPay envoie des données vides alors que le plugin SmartStore
|
||||
Cette erreur indique donc que le webhook est bien accessible depuis BTCPay.<br/>
|
||||
Avec une vraie transaction, on peut donc s'attendre à un fonctionnement correct.
|
||||
]]>
|
||||
</Value>
|
||||
</Value>
|
||||
</LocaleResource>
|
||||
|
||||
<LocaleResource Name="NoteRefund">
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Smartstore.BTCPayServer.Configuration;
|
||||
using Smartstore.Core.Common;
|
||||
using Smartstore.Core.Data;
|
||||
using Smartstore.Engine.Modularity;
|
||||
using Smartstore.Http;
|
||||
|
||||
@ -8,19 +10,48 @@ namespace Smartstore.BTCPayServer
|
||||
{
|
||||
internal class Module : ModuleBase, IConfigurable
|
||||
{
|
||||
private readonly SmartDbContext _smartDbContext;
|
||||
|
||||
public Module(SmartDbContext smartDbContext)
|
||||
{
|
||||
_smartDbContext = smartDbContext;
|
||||
}
|
||||
|
||||
public override async Task InstallAsync(ModuleInstallationContext context)
|
||||
{
|
||||
await SaveSettingsAsync(new BtcPaySettings
|
||||
{
|
||||
BtcPayUrl = "",
|
||||
ApiKey = "",
|
||||
BtcPayStoreID = "",
|
||||
WebHookSecret = ""
|
||||
BtcPayUrl = "", ApiKey = "", BtcPayStoreID = "", WebHookSecret = ""
|
||||
});
|
||||
await ImportLanguageResourcesAsync();
|
||||
|
||||
await AddBTCCurrency(_smartDbContext);
|
||||
await base.InstallAsync(context);
|
||||
}
|
||||
|
||||
private async Task AddBTCCurrency(SmartDbContext smartDbContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
await smartDbContext.Currencies.AddAsync(new Currency
|
||||
{
|
||||
DisplayLocale = "en-US",
|
||||
Name = "Bitcoin",
|
||||
CurrencyCode = "BTC",
|
||||
CustomFormatting = "{0} ₿",
|
||||
Published = true,
|
||||
RoundNumDecimals = 8,
|
||||
|
||||
DisplayOrder = 1,
|
||||
});
|
||||
await smartDbContext.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task UninstallAsync()
|
||||
{
|
||||
await DeleteSettingsAsync<BtcPaySettings>();
|
||||
@ -33,6 +64,6 @@ namespace Smartstore.BTCPayServer
|
||||
|
||||
|
||||
public RouteInfo GetConfigurationRoute()
|
||||
=> new("Configure", "BtcPayAdmin", new { area = "Admin" });
|
||||
=> new("Configure", "BtcPayAdmin", new {area = "Admin"});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,7 @@ using Smartstore.Core;
|
||||
using Smartstore.Core.Checkout.Cart;
|
||||
using Smartstore.Core.Checkout.Orders;
|
||||
using Smartstore.Core.Checkout.Payment;
|
||||
using Smartstore.Core.Common;
|
||||
using Smartstore.Core.Common.Services;
|
||||
using Smartstore.Core.Configuration;
|
||||
using Smartstore.Core.Data;
|
||||
@ -27,7 +28,7 @@ namespace Smartstore.BTCPayServer.Providers
|
||||
[SystemName("Smartstore.BTCPayServer")]
|
||||
[FriendlyName("BTCPayServer")]
|
||||
[Order(1)]
|
||||
public class BTCPayPaymentProvider : PaymentMethodBase, IConfigurable
|
||||
public class BTCPayPaymentProvider : PaymentMethodBase, IConfigurable, IExchangeRateProvider
|
||||
{
|
||||
// https://smartstore.atlassian.net/wiki/spaces/SMNET40/pages/1927643267/How+to+write+a+Payment+Plugin
|
||||
|
||||
@ -132,31 +133,35 @@ namespace Smartstore.BTCPayServer.Providers
|
||||
Customer? myCustomer = await _customerService.GetAuthenticatedCustomerAsync();
|
||||
if (myCustomer == null)
|
||||
{
|
||||
myCustomer = await _db.Customers.FirstOrDefaultAsync(x => x.Id == processPaymentRequest.CustomerId)
|
||||
?? throw new Exception("Customer not found");
|
||||
myCustomer = await _db.Customers.Include(customer => customer.BillingAddress)
|
||||
.FirstOrDefaultAsync(x => x.Id == processPaymentRequest.CustomerId)
|
||||
?? throw new Exception("Customer not found");
|
||||
sEmail = myCustomer.BillingAddress?.Email;
|
||||
sFullName = myCustomer.BillingAddress?.GetFullName();
|
||||
} else
|
||||
sFullName = myCustomer.BillingAddress?.GetFullName();
|
||||
}
|
||||
else
|
||||
{
|
||||
sEmail = myCustomer.Email;
|
||||
sFullName = myCustomer.FullName;
|
||||
}
|
||||
|
||||
var invoice = await _btcPayService.CreateInvoice(settings, new PaymentDataModel()
|
||||
{
|
||||
CurrencyCode = _currencyService.PrimaryCurrency.CurrencyCode,
|
||||
Amount = processPaymentRequest.OrderTotal,
|
||||
BuyerEmail = sEmail,
|
||||
BuyerName = sFullName,
|
||||
OrderID = processPaymentRequest.OrderGuid.ToString(),
|
||||
StoreID = myStore.Id,
|
||||
CustomerID = myCustomer.Id,
|
||||
Description = "From " + myStore.Name,
|
||||
RedirectionURL = myStore.Url + "checkout/completed",
|
||||
Lang = _services.WorkContext.WorkingLanguage.LanguageCulture,
|
||||
OrderUrl = new Uri(new Uri(myStore.Url), _linkGenerator.GetPathByAction("Details", "Order", new {orderId = processPaymentRequest.OrderGuid.ToString()})).ToString()
|
||||
|
||||
}) ;
|
||||
|
||||
var invoice = await _btcPayService.CreateInvoice(settings,
|
||||
new PaymentDataModel()
|
||||
{
|
||||
CurrencyCode = _currencyService.PrimaryCurrency.CurrencyCode,
|
||||
Amount = processPaymentRequest.OrderTotal,
|
||||
BuyerEmail = sEmail,
|
||||
BuyerName = sFullName,
|
||||
OrderID = processPaymentRequest.OrderGuid.ToString(),
|
||||
StoreID = myStore.Id,
|
||||
CustomerID = myCustomer.Id,
|
||||
Description = "From " + myStore.Name,
|
||||
RedirectionURL = myStore.Url + "checkout/completed",
|
||||
Lang = _services.WorkContext.WorkingLanguage.LanguageCulture,
|
||||
OrderUrl = new Uri(new Uri(myStore.Url),
|
||||
_linkGenerator.GetPathByAction("Index", "BtcpayOrder",
|
||||
new {id = processPaymentRequest.OrderGuid})).ToString()
|
||||
});
|
||||
|
||||
result.AuthorizationTransactionResult = invoice.CheckoutLink;
|
||||
result.AuthorizationTransactionId = invoice.Id;
|
||||
@ -275,5 +280,22 @@ namespace Smartstore.BTCPayServer.Providers
|
||||
return new VoidPaymentResult() {NewPaymentStatus = request.Order.PaymentStatus};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<ExchangeRate>> GetCurrencyLiveRatesAsync(string exchangeRateCurrencyCode)
|
||||
{
|
||||
var updateDate = DateTime.UtcNow;
|
||||
var currencies = await _db.Currencies.ToListAsync();
|
||||
var myStore = _services.StoreContext.CurrentStore;
|
||||
var settings = _settingFactory.LoadSettings<BtcPaySettings>(myStore.Id);
|
||||
var client = _btcPayService.GetClient(settings);
|
||||
var pairs = currencies.Select(currency => exchangeRateCurrencyCode + "_" + currency.CurrencyCode).ToArray();
|
||||
var rates = await client.GetStoreRates(settings.BtcPayStoreID,pairs);
|
||||
return rates.Where(result => result.Rate is not null).Select(result => new ExchangeRate()
|
||||
{
|
||||
CurrencyCode = result.CurrencyPair.Split("_")[1],
|
||||
Rate = result.Rate.Value,
|
||||
UpdatedOn = updateDate
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,34 +1,42 @@
|
||||
# BTCPay Plugin for SmartStore
|
||||
# BTCPay plugin for SmartStore - accept Bitcoin payments
|
||||
|
||||

|
||||

|
||||
|
||||
## Plugin Overview
|
||||
|
||||
This plugin allows you to easily integrate Bitcoin payments into your SmartStore website using BTCPay Server. You can configure the plugin either automatically or manually, depending on your preferences and requirements.
|
||||
This plugin allows you to easily integrate Bitcoin payments into your SmartStore website using BTCPay Server — a free, self-hosted and open-source payment gateway solution designed to revolutionize Bitcoin payments. Our seamless integration with SmartStore ensures a hassle-free connection to your self-hosted BTCPay Server.
|
||||
|
||||
Experience the simplicity of accepting Bitcoin payments with just a few straightforward steps. You can configure the plugin either automatically or manually, depending on your preferences and requirements.
|
||||
|
||||
## Automatic Configuration
|
||||
|
||||
1. Enter the "BTCPay Url" parameter and save.2. Click on the "Create API Key Automatically" link to be redirected to the key creation page on your BTCPay server.
|
||||
3. The "API Key" and "BTCPay Store ID" parameters will be automatically filled and a webhook will be automatically created. Save.
|
||||
|
||||
1. Enter Url to your BTCPay Server into "BTCPay Url" field. (e.g. https://mainnet.demo.btcpayserver.org)
|
||||
2. Click on the "Configure automatically" button to be redirected to the API authorization page on your BTCPay server
|
||||
3. On BTCPay authorization page: Select the store you want to connect to your Smartstore (you might need to login first)
|
||||
4. Click on "Authorize App" button and you will be redirected back to your Smartstore
|
||||
3. The "API Key", "BTCPay Store ID" and "Webhook Secret" fields will be automatically filled and a webhook created
|
||||
4. Click "Save" button at the top right to persist the configuration
|
||||
5. Congrats, the configuration is now done.
|
||||
|
||||
## Manual Configuration
|
||||
|
||||
Ensure that the following fields are filled out: "BTCPay Url," "API Key," "BTCPay Store ID," and "WebHook Secret."
|
||||
|
||||
To create the BTCPay API key, [read this](https://docs.btcpayserver.org/VirtueMart/#22-create-an-api-key-and-configure-permissions).
|
||||
- Note: If you want to use the Refund feature, you must also add the "Modify your stores" permission. After a refund, an order note is created, indicating the BTCPay link where the customer can request a refund.
|
||||
- Note: If you want to use the Refund feature, you must also add the "Create non-approved pull payments" permission. After a refund, an order note is created where you can copy the pull payments link and send to your customer. The customer can request the refund on that page.
|
||||
|
||||
To create the BTCPay WebHook, [read this](https://docs.btcpayserver.org/VirtueMart/#23-create-a-webhook-on-btcpay-server) and use the default secret code generated by BTCPay.
|
||||
- Note: Other than in the guide you need to copy the Url shown in field "Webhook Url" from your configuration screen on Smartstore.
|
||||
|
||||
## Support and Documentation
|
||||
## Support
|
||||
|
||||
For more information and detailed instructions, please visit the [official documentation](https://docs.btcpayserver.org/).
|
||||
Feel free to join our support channel over at [Mattermost Chat](https://chat.btcpayserver.org/) if you need help or have any further questions.
|
||||
|
||||
If experience a bug please open an issue in our repository [here](https://github.com/btcpayserver/Smartstore.BTCPayServer/issues)
|
||||
|
||||
## License
|
||||
|
||||
This plugin is released under the [MIT License](LICENSE).
|
||||
|
||||
---
|
||||
For more information and updates, visit our [GitHub repository](https://github.com/Nisaba/btcpay-smartstore-plugin).
|
||||
|
||||
Find our latest releases on the [Smartstore marketplace](https://community.smartstore.com/index.php?/files/file/246-btcpay-server-for-smartstore/) or on our [release page](https://github.com/btcpayserver/Smartstore.BTCPayServer/releases)
|
||||
|
||||
@ -11,13 +11,11 @@ using Smartstore.Core.Checkout.Payment;
|
||||
|
||||
namespace Smartstore.BTCPayServer.Services
|
||||
{
|
||||
|
||||
|
||||
public class BtcPayService
|
||||
{
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
|
||||
public BtcPayService(IHttpClientFactory httpClientFactory )
|
||||
public BtcPayService(IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
}
|
||||
@ -25,12 +23,13 @@ namespace Smartstore.BTCPayServer.Services
|
||||
|
||||
public BTCPayServerClient GetClient(BtcPaySettings settings)
|
||||
{
|
||||
return new BTCPayServerClient(new Uri(settings.BtcPayUrl), settings.ApiKey,_httpClientFactory.CreateClient("BTCPayServer"));
|
||||
return new BTCPayServerClient(new Uri(settings.BtcPayUrl), settings.ApiKey,
|
||||
_httpClientFactory.CreateClient("BTCPayServer"));
|
||||
}
|
||||
|
||||
|
||||
public async Task<string> GetStoreId(BtcPaySettings settings)
|
||||
{
|
||||
return (await GetClient(settings).GetStores()).First().Id;
|
||||
return (await GetClient(settings).GetStores()).First().Id;
|
||||
}
|
||||
|
||||
public static bool CheckSecretKey(string key, string message, string signature)
|
||||
@ -42,7 +41,6 @@ namespace Smartstore.BTCPayServer.Services
|
||||
|
||||
public async Task<InvoiceData> CreateInvoice(BtcPaySettings settings, PaymentDataModel paymentData)
|
||||
{
|
||||
|
||||
var client = GetClient(settings);
|
||||
var req = new CreateInvoiceRequest()
|
||||
{
|
||||
@ -55,7 +53,7 @@ namespace Smartstore.BTCPayServer.Services
|
||||
RedirectAutomatically = true,
|
||||
RequiresRefundEmail = false
|
||||
},
|
||||
Metadata = JObject.FromObject(new
|
||||
Metadata = JObject.FromObject(new
|
||||
{
|
||||
buyerEmail = paymentData.BuyerEmail,
|
||||
buyerName = paymentData.BuyerName,
|
||||
@ -63,18 +61,15 @@ namespace Smartstore.BTCPayServer.Services
|
||||
orderUrl = paymentData.OrderUrl,
|
||||
itemDesc = paymentData.Description,
|
||||
}),
|
||||
Receipt = new InvoiceDataBase.ReceiptOptions()
|
||||
{
|
||||
Enabled = true,
|
||||
}
|
||||
Receipt = new InvoiceDataBase.ReceiptOptions() {Enabled = true,}
|
||||
};
|
||||
|
||||
var invoice = await client.CreateInvoice(settings.BtcPayStoreID, req);
|
||||
return invoice;
|
||||
}
|
||||
|
||||
public async Task<string> CreateRefund(BtcPaySettings settings, RefundPaymentRequest refundRequest)
|
||||
{
|
||||
|
||||
var client = GetClient(settings);
|
||||
var invoice = await client.GetInvoicePaymentMethods(settings.BtcPayStoreID,
|
||||
refundRequest.Order.AuthorizationTransactionId);
|
||||
@ -102,33 +97,46 @@ namespace Smartstore.BTCPayServer.Services
|
||||
refundRequest.Order.AuthorizationTransactionId, refundInvoiceRequest);
|
||||
|
||||
return refund.ViewLink;
|
||||
|
||||
}
|
||||
|
||||
public async Task<string> CreateWebHook(BtcPaySettings settings, string WebHookUrl)
|
||||
public async Task<string> CreateWebHook(BtcPaySettings settings, string webHookUrl)
|
||||
{
|
||||
var client = GetClient(settings);
|
||||
var response = await client.CreateWebhook(settings.BtcPayStoreID, new CreateStoreWebhookRequest()
|
||||
var existing = await client.GetWebhooks(settings.BtcPayStoreID);
|
||||
var existingWebHook = existing.Where(x => x.Url == webHookUrl);
|
||||
foreach (var webhookData in existingWebHook)
|
||||
{
|
||||
Url = WebHookUrl,
|
||||
Enabled = true,
|
||||
|
||||
});
|
||||
await client.DeleteWebhook(settings.BtcPayStoreID, webhookData.Id);
|
||||
}
|
||||
|
||||
var response = await client.CreateWebhook(settings.BtcPayStoreID,
|
||||
new CreateStoreWebhookRequest()
|
||||
{
|
||||
Url = webHookUrl,
|
||||
Enabled = true,
|
||||
AuthorizedEvents = new StoreWebhookBaseData.AuthorizedEventsData()
|
||||
{
|
||||
SpecificEvents = new[]
|
||||
{
|
||||
WebhookEventType.InvoiceReceivedPayment, WebhookEventType.InvoiceProcessing,
|
||||
WebhookEventType.InvoiceExpired, WebhookEventType.InvoiceSettled,
|
||||
WebhookEventType.InvoiceInvalid, WebhookEventType.InvoicePaymentSettled,
|
||||
}
|
||||
}
|
||||
});
|
||||
return response.Secret;
|
||||
}
|
||||
|
||||
public async Task<InvoiceData> GetInvoice(BtcPaySettings settings, string invoiceId)
|
||||
{
|
||||
|
||||
var client = GetClient(settings);
|
||||
return await client.GetInvoice(settings.BtcPayStoreID, invoiceId);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateOrderWithInvoice(BtcPaySettings settings ,Order order, string invoiceId)
|
||||
public async Task<bool> UpdateOrderWithInvoice(BtcPaySettings settings, Order order, string invoiceId)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var invoice = await GetInvoice(settings, invoiceId);
|
||||
return await UpdateOrderWithInvoice(order, invoice, null);
|
||||
}
|
||||
@ -141,7 +149,7 @@ namespace Smartstore.BTCPayServer.Services
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> UpdateOrderWithInvoice(Order order, InvoiceData invoiceData,
|
||||
WebhookInvoiceEvent? webhookEvent)
|
||||
{
|
||||
@ -158,7 +166,7 @@ namespace Smartstore.BTCPayServer.Services
|
||||
newPaymentStatus = PaymentStatus.Pending;
|
||||
break;
|
||||
case InvoiceStatus.Processing:
|
||||
newPaymentStatus =PaymentStatus.Pending; // PaymentStatus.Authorized; smartstore will set the order to processing otherwise
|
||||
newPaymentStatus = PaymentStatus.Authorized;
|
||||
newOrderStatus = OrderStatus.Pending;
|
||||
break;
|
||||
case InvoiceStatus.Expired:
|
||||
@ -191,40 +199,53 @@ namespace Smartstore.BTCPayServer.Services
|
||||
updated = true;
|
||||
}
|
||||
|
||||
var additionalMessage = GetAdditionalMessageFromWebhook(webhookEvent);
|
||||
if (updated)
|
||||
{
|
||||
var aditionalData = GetAdditionalMessageFromWebhook(webhookEvent)?.message;
|
||||
aditionalData = string.IsNullOrEmpty(aditionalData) ? "" : $" - {aditionalData}";
|
||||
additionalMessage = string.IsNullOrEmpty(additionalMessage) ? "" : $" - {additionalMessage}";
|
||||
order.AddOrderNote(
|
||||
$"BTCPayServer: Order status updated to {newOrderStatus} and payment status to {newPaymentStatus} by BTCPay with invoice {invoiceData.Id}{aditionalData}",
|
||||
$"BTCPayServer: Order status updated to {newOrderStatus} and payment status to {newPaymentStatus} by BTCPay with invoice {invoiceData.Id}{additionalMessage}",
|
||||
false);
|
||||
order.HasNewPaymentNotification = true;
|
||||
|
||||
if (order.PaymentStatus == PaymentStatus.Paid && !string.IsNullOrEmpty(order.CaptureTransactionResult))
|
||||
if (order.PaymentStatus is PaymentStatus.Authorized or PaymentStatus.Paid &&
|
||||
!string.IsNullOrEmpty(order.CaptureTransactionResult))
|
||||
order.AddOrderNote(
|
||||
$"BTCPayServer: Payment received. <a href='{order.CaptureTransactionResult}'>Click here for more information.</a>", true);
|
||||
$"BTCPayServer: Payment received {(order.PaymentStatus is PaymentStatus.Authorized ? $"but waiting to confirm. <a href='{order.AuthorizationTransactionResult}'>Click here for more information.</a>" : $". <a href='{order.CaptureTransactionResult}'>Click here for more information.</a>")}", true);
|
||||
return true;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(additionalMessage))
|
||||
{
|
||||
order.AddOrderNote(
|
||||
$"BTCPayServer: {additionalMessage}", false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private (string message, bool customerFriendly)? GetAdditionalMessageFromWebhook(WebhookInvoiceEvent? webhookEvent)
|
||||
private string? GetAdditionalMessageFromWebhook(WebhookInvoiceEvent? webhookEvent)
|
||||
{
|
||||
switch (webhookEvent?.Type)
|
||||
{
|
||||
case WebhookEventType.InvoiceReceivedPayment when webhookEvent.ReadAs<WebhookInvoiceReceivedPaymentEvent>() is { } receivedPaymentEvent:
|
||||
return ($"Payment detected ({receivedPaymentEvent.PaymentMethod}: {receivedPaymentEvent.Payment.Value})" , false);
|
||||
case WebhookEventType.InvoicePaymentSettled when webhookEvent.ReadAs<WebhookInvoicePaymentSettledEvent>() is { } receivedPaymentEvent:
|
||||
return ($"Payment settled ({receivedPaymentEvent.PaymentMethod}: {receivedPaymentEvent.Payment.Value})" , false);
|
||||
case WebhookEventType.InvoiceProcessing when webhookEvent.ReadAs<WebhookInvoiceProcessingEvent>() is { } receivedPaymentEvent && receivedPaymentEvent.OverPaid:
|
||||
return ($"Invoice was overpaid." , false);
|
||||
case WebhookEventType.InvoiceExpired when webhookEvent.ReadAs<WebhookInvoiceExpiredEvent>() is { } receivedPaymentEvent && receivedPaymentEvent.PartiallyPaid:
|
||||
return ($"Invoice expired but was paid partially, please check." , false);
|
||||
default: return null;
|
||||
}
|
||||
switch (webhookEvent?.Type)
|
||||
{
|
||||
case WebhookEventType.InvoiceReceivedPayment
|
||||
when webhookEvent.ReadAs<WebhookInvoiceReceivedPaymentEvent>() is { } receivedPaymentEvent:
|
||||
return
|
||||
$"Payment detected ({receivedPaymentEvent.PaymentMethod}: {receivedPaymentEvent.Payment.Value})";
|
||||
case WebhookEventType.InvoicePaymentSettled
|
||||
when webhookEvent.ReadAs<WebhookInvoicePaymentSettledEvent>() is { } receivedPaymentEvent:
|
||||
return
|
||||
$"Payment settled ({receivedPaymentEvent.PaymentMethod}: {receivedPaymentEvent.Payment.Value})";
|
||||
case WebhookEventType.InvoiceProcessing
|
||||
when webhookEvent.ReadAs<WebhookInvoiceProcessingEvent>() is { } receivedPaymentEvent &&
|
||||
receivedPaymentEvent.OverPaid:
|
||||
return $"Invoice was overpaid.";
|
||||
case WebhookEventType.InvoiceExpired
|
||||
when webhookEvent.ReadAs<WebhookInvoiceExpiredEvent>() is { } receivedPaymentEvent &&
|
||||
receivedPaymentEvent.PartiallyPaid:
|
||||
return $"Invoice expired but was paid partially.";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -139,27 +139,30 @@
|
||||
<span asp-validation-for="WebHookSecret" />
|
||||
</div>
|
||||
</div>
|
||||
<span></span>
|
||||
<br/>
|
||||
|
||||
<div class="adminRow">
|
||||
<div class="adminTitle">
|
||||
<smart-label asp-for="AdditionalFee" />
|
||||
<hr/>
|
||||
<div class="card border-0" id="additionalOptions">
|
||||
<h6 class="card-title" >Additional Options</h6>
|
||||
|
||||
<div class="adminRow">
|
||||
<div class="adminTitle">
|
||||
<smart-label asp-for="AdditionalFee"/>
|
||||
</div>
|
||||
<div class="adminData">
|
||||
<editor asp-for="AdditionalFee" sm-postfix="@ViewBag.StoreCurrencyCode"/>
|
||||
<span asp-validation-for="AdditionalFee"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="adminRow">
|
||||
<div class="adminTitle">
|
||||
<smart-label asp-for="AdditionalFeePercentage"/>
|
||||
</div>
|
||||
<div class="adminData">
|
||||
<editor asp-for="AdditionalFeePercentage"/>
|
||||
<span asp-validation-for="AdditionalFeePercentage"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="adminData">
|
||||
<editor asp-for="AdditionalFee" sm-postfix="@ViewBag.StoreCurrencyCode" />
|
||||
<span asp-validation-for="AdditionalFee"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="adminRow">
|
||||
<div class="adminTitle">
|
||||
<smart-label asp-for="AdditionalFeePercentage" />
|
||||
</div>
|
||||
<div class="adminData">
|
||||
<editor asp-for="AdditionalFeePercentage" />
|
||||
<span asp-validation-for="AdditionalFeePercentage"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"SystemName": "Smartstore.BTCPayServer",
|
||||
"FriendlyName": "Bitcoin payments with BTCPay Server",
|
||||
"Description": "This plugin enables Bitcoin payments with your BTCPay Server instance",
|
||||
"Version": "5.0.6",
|
||||
"Version": "5.0.5.1",
|
||||
"MinAppVersion": "5.0.5",
|
||||
"Author": "Nisaba Solutions",
|
||||
"Order": 1,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
28
tools/Smartstore.PackagerCLI/Program.cs
Normal file
28
tools/Smartstore.PackagerCLI/Program.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using Smartstore.Engine.Modularity;
|
||||
using Smartstore.IO;
|
||||
|
||||
var modulePath = args[0];
|
||||
var buildPath = args[1];
|
||||
var dirInfo = new DirectoryInfo(modulePath);
|
||||
|
||||
var lfs = new LocalFileSystem(dirInfo.ToString());
|
||||
var descriptor = ModuleDescriptor.Create(new LocalDirectory("/", dirInfo,lfs), new LocalFileSystem(dirInfo.Parent.ToString()));
|
||||
|
||||
var pb = new Smartstore.Core.Packaging.PackageBuilder(new LocalFileSystem(dirInfo.Parent.Parent.ToString()));
|
||||
var package = await pb.BuildPackageAsync(descriptor);
|
||||
var fileName = package.FileName;
|
||||
|
||||
if (!Directory.Exists(buildPath))
|
||||
{
|
||||
Directory.CreateDirectory(buildPath);
|
||||
}
|
||||
|
||||
fileName = Path.Combine(buildPath, fileName);
|
||||
|
||||
await using (var stream = File.Create(fileName))
|
||||
{
|
||||
await package.ArchiveStream.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
var fileInfo = new FileInfo(fileName);
|
||||
Console.Write($"{fileInfo.FullName}");
|
||||
23
tools/Smartstore.PackagerCLI/Smartstore.PackagerCLI.csproj
Normal file
23
tools/Smartstore.PackagerCLI/Smartstore.PackagerCLI.csproj
Normal file
@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Smartstore.Core\Smartstore.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\build-btcpayplugin.ps1">
|
||||
<Link>build-btcpayplugin.ps1</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\build-btcpayplugin.sh">
|
||||
<Link>build-btcpayplugin.sh</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Loading…
Reference in New Issue
Block a user