commit 0b967ad6a521f07a39d2b0f06e092ac0b52e4a08
parent cfebfbae19164cc719666582645b963262c36b3d
Author: NicholasFarrow <nicholas.w.farrow@gmail.com>
Date: Fri, 15 Jan 2021 02:06:42 +1100
Add webhook for payment confirmation
Diffstat:
M | gateways/btcpyment.php | | | 105 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
M | server.py | | | 52 | ++++++++++++++++++++++++++++++++++++---------------- |
2 files changed, 90 insertions(+), 67 deletions(-)
diff --git a/gateways/btcpyment.php b/gateways/btcpyment.php
@@ -48,10 +48,10 @@ function btcpyment_init_gateway_class() {
*/
public function __construct() {
- $this->id = 'btcpyment'; // payment gateway plugin ID
+ $this->id = 'BTCPyment'; // payment gateway plugin ID
$this->icon = ''; // URL of the icon that will be displayed on checkout page near your gateway name
$this->has_fields = true; // in case you need a custom credit card form
- $this->method_title = 'btcpyment Gateway';
+ $this->method_title = 'BTCPyment Gateway';
$this->method_description = 'Description of btcpyment payment gateway'; // will be displayed on the options page
// gateways can support subscriptions, refunds, saved payment methods,
@@ -68,9 +68,12 @@ function btcpyment_init_gateway_class() {
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->enabled = $this->get_option( 'enabled' );
+ $this->btcpyment_server_url = $this->get_option( 'btcpyment_server_url' );
+ $this->redirect_url = $this->get_option( 'redirect_url' );
$this->testmode = 'yes' === $this->get_option( 'testmode' );
$this->private_key = $this->testmode ? $this->get_option( 'test_private_key' ) : $this->get_option( 'private_key' );
$this->publishable_key = $this->testmode ? $this->get_option( 'test_publishable_key' ) : $this->get_option( 'publishable_key' );
+ $this->callback_URL = str_replace( 'https:', 'http:', add_query_arg( 'wc-api', 'wc_api_btcpyment)', home_url( '/' ) ) );
// This action hook saves the settings
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
@@ -79,7 +82,7 @@ function btcpyment_init_gateway_class() {
add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
// You can also register a webhook here
- add_action( 'woocommerce_api_btcpyment', array( $this, 'webhook' ) );
+ add_action( 'wc_api_btcpyment', array( $this, 'webhook' ) );
}
/**
@@ -108,6 +111,16 @@ function btcpyment_init_gateway_class() {
'description' => 'This controls the description which the user sees during checkout.',
'default' => 'Pay with Bitcoin via BTCPyment',
),
+ 'btcpyment_server_url' => array(
+ 'title' => 'BTCPyment URL',
+ 'type' => 'text',
+ 'description' => 'Points towards your instance of BTCPyment, should be IP or https://SERVER.com'
+ ),
+ 'redirect_url' => array(
+ 'title' => 'Redirect URL',
+ 'type' => 'text',
+ 'description' => 'URL the user is redirected to after payment.'
+ ),
'testmode' => array(
'title' => 'Test mode',
'label' => 'Enable Test Mode',
@@ -192,67 +205,57 @@ function btcpyment_init_gateway_class() {
*/
$args = array(
'amount' => $order->get_total(),
- 'id' => $order->get_id()
+ 'id' => $order->get_id(),
+ 'webhook_url' => $this->callback_URL
// HASH??? FOR SECURE PAYMENTS?
);
/*
* Your API interaction could be built with wp_remote_post()
*/
- $redir_url = add_query_arg(
+ write_log($this->callback_URL);
+ $payment_url = add_query_arg(
$args,
- 'https://btcpyment.nickfarrow.com/pay'
+ $this->btcpyment_server_url . "/pay"
);
- write_log($redir_url);
-
- // $response = wp_remote_post( 'https://btcpyment.nickfarrow.com/', $args );
- $response = wp_remote_post($redir_url);
-
- write_log($response);
- //
- // $querystring = http_build_query( $payload );
-
- // echo '<script>console.log("PHP error: ' . implode(", ", $redir_url) . '")</script>';
- // echo '<script>console.log("PHP error: ' . implode(", ", $response) . '")</script>';
-
return [
'result' => 'success',
- 'redirect' => $redir_url
+ 'redirect' => $payment_url
];
- if( !is_wp_error( $response ) ) {
-
- $body = json_decode( $response['body'], true );
-
- // it could be different depending on your payment processor
- if ( $body['response']['responseCode'] == 'APPROVED' ) {
-
- // we received the payment
- $order->payment_complete();
- $order->reduce_order_stock();
-
- // some notes to customer (replace true with false to make it private)
- $order->add_order_note( 'Hey, your order is paid! Thank you!', true );
-
- // Empty cart
- $woocommerce->cart->empty_cart();
-
- // Redirect to the thank you page
- return array(
- 'result' => 'success',
- 'redirect' => $this->get_return_url( $order )
- );
-
- } else {
- wc_add_notice( 'Please try again.', 'error' );
- return;
- }
-
- } else {
- wc_add_notice( 'Connection error.', 'error' );
- return;
- }
+ // if( !is_wp_error( $response ) ) {
+ //
+ // $body = json_decode( $response['body'], true );
+ //
+ // // it could be different depending on your payment processor
+ // if ( $body['response']['responseCode'] == 'APPROVED' ) {
+ //
+ // // we received the payment
+ // $order->payment_complete();
+ // $order->reduce_order_stock();
+ //
+ // // some notes to customer (replace true with false to make it private)
+ // $order->add_order_note( 'Hey, your order is paid! Thank you!', true );
+ //
+ // // Empty cart
+ // $woocommerce->cart->empty_cart();
+ //
+ // // Redirect to the thank you page
+ // return array(
+ // 'result' => 'success',
+ // 'redirect' => $this->get_return_url( $order )
+ // );
+ //
+ // } else {
+ // wc_add_notice( 'Please try again.', 'error' );
+ // return;
+ // }
+ //
+ // } else {
+ // wc_add_notice( 'Connection error.', 'error' );
+ // return;
+ // }
}
diff --git a/server.py b/server.py
@@ -2,6 +2,8 @@ from flask import Flask, render_template, session, request
from flask_socketio import SocketIO, emit, disconnect
from markupsafe import escape
import time
+import os
+import requests
import ssh_tunnel
import config
@@ -12,21 +14,25 @@ from pay import lnd
# Begin websocket
async_mode = None
app = Flask(__name__)
-app.config['SECRET_KEY'] = 'secret!'
+app.config['SECRET_KEY'] = os.urandom(24).hex()
+print("Initialised Flask with secret key: {}".format(app.config['SECRET_KEY']))
socket_ = SocketIO(app, async_mode=async_mode, cors_allowed_origins="*")
-# Render html
+# Render index pages
+# To-do, this will be a donation form page that submits to /pay
@app.route('/')
def index():
- # TODO
- # DONATION STYLE FORM PAGE
return render_template('index.html', async_mode=socket_.async_mode)
@app.route('/pay')
def payment_page():
- amount = request.args.get('amount')
- id = amount = request.args.get('id')
- return render_template('index.html', amount=amount, id=id, async_mode=socket_.async_mode)
+ #
+ # # Label is blank if not supplied
+ # params = {'label':''}
+ # for key, value in dict(request.args).items():
+ # params[key] = value
+ params = dict(request.args)
+ return render_template('index.html', params=params, async_mode=socket_.async_mode)
# Basic return on initialisation
@socket_.on('initialise')
@@ -37,8 +43,6 @@ def test_message(message):
# Recieves form amount and initiates invoice and payment processing.
@socket_.on('make_payment')
def make_payment(payload):
- print("Requesting payment for {}".format(payload['amount']))
-
# Check the amount is a float
amount = payload['amount']
try:
@@ -54,8 +58,12 @@ def make_payment(payload):
amount = None
return
- # Need to check this is safe!
- label = payload['label']
+ # Return if label missing
+ if 'label' not in payload.keys():
+ return
+ # label = payload['label']
+ # else:
+ # label = "undefined"
# Initialise this payment
payment = create_invoice(amount, "USD", label)
@@ -69,6 +77,19 @@ def make_payment(payload):
invoice.success.success()
+ # Call webhook
+ response = requests.post(
+ payload['webhook_url'], data={'id' : label},
+ headers={'Content-Type': 'application/json'}
+ )
+ if response.status_code != 200:
+ raise ValueError(
+ 'Failed to confirm payment via webhook %s, the response is:\n%s'
+ % (response.status_code, response.text)
+ )
+ else:
+ print("Successfully confirmed payment via webhook.")
+
### DO SOMETHING
# Depends on config
# Get redirected?
@@ -109,8 +130,8 @@ def update_status(payment, console_status=True):
# Payment processing function.
# Handle payment logic.
def process_payment(payment):
- payment.status = 'Awaiting payment.'
- payment.response = 'Awaiting payment.'
+ payment.status = 'Payment intialised, awaiting payment.'
+ payment.response = 'Payment intialised, awaiting payment.'
update_status(payment)
# Track start_time for payment timeouts
@@ -138,8 +159,8 @@ def process_payment(payment):
update_status(payment, console_status=False)
socket_.sleep(config.pollrate)
else:
- payment.status = "Awaiting payment...".format(payment.value)
- payment.response = "Awaiting payment...".format(payment.value)
+ payment.status = "Waiting for payment...".format(payment.value)
+ payment.response = "Waiting for payment...".format(payment.value)
update_status(payment)
socket_.sleep(config.pollrate)
else:
@@ -159,5 +180,4 @@ print("Connection successful.")
if __name__ == '__main__':
- socket_.run(app, debug=True)
socket_.run(app, debug=False)