SatSale

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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:
Mgateways/btcpyment.php | 105+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mserver.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)