def conditions_fulfilled(self):
        if self.txid is None:
            return False

        data = transaction(txid=self.txid)
        if isinstance(
                data, dict
        ) and 'transaction' in data and 'confirmations' in data['transaction']:
            confirmations = data['transaction']['confirmations']
        else:
            # Something went wrong during retrieval of transaction
            return False

        return True if self.confirmations <= confirmations else False
    def run(self):
        LOG.info('Running Spellbook Script: %s' %
                 os.path.splitext(os.path.basename(__file__))[0])

        if self.json is not None:
            if 'payment_request_id' not in self.json:
                LOG.error(
                    'Payment request json does not contain the payment_request_id!'
                )
                return

            if 'txid' not in self.json:
                LOG.error('Payment request json does not contain the txid!')
                return
            payment_request_id = self.json['payment_request_id']

            try:
                payment_request = PaymentRequest(
                    payment_request_id=payment_request_id)
            except Exception as ex:
                self.http_response = {'error': str(ex)}
                return

            # Note: because this is just an example app, we will ignore the possibility of transaction malleability
            txid = self.json['txid']
            LOG.info('Payment request %s received a transaction: %s' %
                     (payment_request_id, txid))

            tx_data = transaction(txid=txid)
            if 'transaction' not in tx_data:
                LOG.error('Unable to retrieve transaction %s' % txid)
                return

            tx = tx_data['transaction']

            transaction_received = False

            # Note: we are assuming there is only a single output to the payment address
            for tx_output in tx['outputs']:
                if tx_output[
                        'address'] == payment_request.address and tx_output[
                            'value'] == payment_request.amount_btc:
                    LOG.info('Transaction contains valid payment!')
                    transaction_received = True
                    payment_request.txid = txid
                    payment_request.status = 'Transaction received'
                elif tx_output[
                        'address'] == payment_request.address and tx_output[
                            'value'] < payment_request.amount_btc:
                    LOG.info('Transaction has less than the requested amount!')
                    payment_request.status = 'Insufficient amount received'
                elif tx_output[
                        'address'] == payment_request.address and tx_output[
                            'value'] > payment_request.amount_btc:
                    LOG.info('Transaction has more than the requested amount!')
                    transaction_received = True
                    payment_request.txid = txid
                    payment_request.status = 'Excess amount received'

            if transaction_received is False:
                LOG.info('Transaction does not contain a valid payment!')
                return

            payment_request.confirmations = tx['confirmations']
            if payment_request.confirmations >= 6:
                payment_request.status = 'Confirmed'

            payment_request.save()

            # Send an email to notify we received the transaction
            action = get_action(action_id='tx_received_email',
                                action_type=ActionType.SENDMAIL)
            action.mail_subject = 'Transaction received for payment %s' % payment_request.payment_request_id
            action.mail_recipients = NOTIFICATION_EMAIL
            action.mail_body_template = os.path.join(
                'PaymentProcessor', 'templates', 'TransactionReceived.txt'
            )  # The spellbook will search for the template in the 'email_templates' and in the 'apps' directory, subdirectories are allowed, just need to specify the full path as shown here
            action.mail_variables = {
                'PAYMENT_REQUEST_ID':
                payment_request.payment_request_id,
                'SELLER_ID':
                payment_request.seller_id,
                'AMOUNT_FIAT':
                payment_request.amount_fiat,
                'CURRENCY':
                payment_request.currency,
                'NOTE':
                payment_request.note,
                'ADDRESS':
                payment_request.address,
                'AMOUNT_BTC':
                payment_request.amount_btc /
                1e8,  # amount is in satoshis, display in BTC
                'PRICE_BTC':
                payment_request.price_btc,
                'PRICE_TIMESTAMP':
                datetime.fromtimestamp(
                    payment_request.price_timestamp).strftime(
                        '%Y-%m-%d %H:%M:%S'),
                'TXID':
                payment_request.txid
            }

            action.run()
예제 #3
0
 def get_transaction(txid):
     response.content_type = 'application/json'
     return transaction(txid)
    def run(self):
        LOG.info('Running Spellbook Script: %s' %
                 os.path.splitext(os.path.basename(__file__))[0])
        LOG.info('triggered: %s' % self.triggered)

        # If the trigger contains data, override the information in self.json
        if self.data is not None:
            if self.json is None:
                self.json = {}

            for key, value in self.data.items():
                self.json[key] = value

        if self.json is not None:

            if 'payment_request_id' not in self.json:
                LOG.error(
                    'Payment request json does not contain the payment request id!'
                )
                return

            try:
                payment_request = PaymentRequest(
                    payment_request_id=self.json['payment_request_id'])
            except Exception as ex:
                self.http_response = {'error': str(ex)}
                return

            if payment_request.txid is not None:
                tx_data = transaction(txid=payment_request.txid)
                tx = tx_data['transaction'] if 'transaction' in tx_data else {}
                payment_request.confirmations = tx[
                    'confirmations'] if 'confirmations' in tx else None

            else:
                # Fallback method in case the listener did not pick up the transaction (tx might have happened after listener timed out)
                balance_data = balance(address=payment_request.address)

                # If the final balance of the address is equal or more than the requested amount, assume the most recent transaction is the payment transaction
                # Note: there could be situations where multiple transactions are sent, those situations are not handled in this example app
                if 'balance' in balance_data and balance_data['balance'][
                        'final'] >= payment_request.amount_btc:
                    LOG.info(
                        'Current balance of %s: %s' %
                        (payment_request.address, balance_data['balance']))
                    transactions_data = transactions(
                        address=payment_request.address)
                    if 'transactions' in transactions_data:
                        tx = transactions_data['transactions'][-1]
                        payment_request.txid = tx['txid']
                        payment_request.confirmations = tx['confirmations']

            if payment_request.confirmations >= 6:
                payment_request.status = 'Confirmed'
            elif 1 <= payment_request.confirmations < 6:
                payment_request.status = '%s of 6 confirmations' % payment_request.confirmations

            payment_request.save()

            LOG.info('Retrieving status of payment request: %s' %
                     payment_request.payment_request_id)
            LOG.info('Seller id: %s' % payment_request.seller_id)
            LOG.info('Fiat Amount: %s %s' %
                     (payment_request.amount_fiat, payment_request.currency))
            LOG.info('BTC Amount: %s (price %s %s/BTC @ %s)' %
                     (payment_request.amount_btc, payment_request.price_btc,
                      payment_request.currency,
                      datetime.fromtimestamp(payment_request.price_timestamp
                                             ).strftime('%Y-%m-%d %H:%M:%S')))
            LOG.info('Note: %s' % payment_request.note)
            LOG.info('Address: %s' % payment_request.address)
            LOG.info('Status: %s' % payment_request.status)
            LOG.info('Txid: %s' % payment_request.txid)
            LOG.info('Confirmations: %s' % payment_request.confirmations)

            self.http_response = payment_request.json_encodable()

            if payment_request.status == 'Confirmed':
                # Send an email to notify the payment is confirmed
                action = get_action(action_id='tx_received_email',
                                    action_type=ActionType.SENDMAIL)
                action.mail_subject = 'Payment request %s is confirmed' % payment_request.payment_request_id
                action.mail_recipients = NOTIFICATION_EMAIL
                action.mail_body_template = os.path.join(
                    'PaymentProcessor', 'templates', 'PaymentConfirmed.txt'
                )  # The spellbook will search for the template in the 'email_templates' and in the 'apps' directory, subdirectories are allowed, just need to specify the full path as shown here
                action.mail_variables = {
                    'PAYMENT_REQUEST_ID':
                    payment_request.payment_request_id,
                    'SELLER_ID':
                    payment_request.seller_id,
                    'AMOUNT_FIAT':
                    payment_request.amount_fiat,
                    'CURRENCY':
                    payment_request.currency,
                    'NOTE':
                    payment_request.note,
                    'ADDRESS':
                    payment_request.address,
                    'AMOUNT_BTC':
                    payment_request.amount_btc /
                    1e8,  # amount is in satoshis, display in BTC
                    'PRICE_BTC':
                    payment_request.price_btc,
                    'PRICE_TIMESTAMP':
                    datetime.fromtimestamp(
                        payment_request.price_timestamp).strftime(
                            '%Y-%m-%d %H:%M:%S'),
                    'TXID':
                    payment_request.txid
                }

                action.run()

                # Now that the payment is complete and confirmation email is sent, we can delete the trigger by setting
                # the self_destruct time to something in the past, this will delete the trigger next time check_triggers happens
                # We can not delete it right now because it is still in use by the spellbookserver
                trigger = get_trigger(
                    trigger_id=payment_request.payment_request_id,
                    trigger_type=TriggerType.BALANCE)
                trigger.self_destruct = int(time.time()) - 1
                trigger.save()