def address_webhook(request, secret_key, ignored_key): ''' Process an inbound webhook from blockcypher ''' # Log webhook webhook = WebHook.log_webhook(request, WebHook.BLOCKCYPHER_ADDRESS_NOTIFICATION) assert secret_key == WEBHOOK_SECRET_KEY assert request.method == 'POST', 'Request has no post' blockcypher_id = request.META.get('HTTP_X_EVENTID') assert 'tx-confirmation' == request.META.get('HTTP_X_EVENTTYPE') payload = json.loads(request.body.decode()) address_subscription = AddressSubscription.objects.get( blockcypher_id=blockcypher_id) tx_hash = payload['hash'] num_confs = payload['confirmations'] double_spend = payload['double_spend'] satoshis_sent = payload['total'] fee_in_satoshis = payload['fees'] tx_event = get_object_or_None( OnChainTransaction, tx_hash=tx_hash, address_subscription=address_subscription, ) if tx_event: tx_event.num_confs = num_confs tx_event.double_spend = double_spend tx_event.save() else: tx_event = OnChainTransaction.objects.create( tx_hash=tx_hash, address_subscription=address_subscription, num_confs=num_confs, double_spend=double_spend, satoshis_sent=satoshis_sent, fee_in_satoshis=fee_in_satoshis, ) tx_event.send_email_notification() # Update logging webhook.address_subscription = address_subscription webhook.succeeded = True webhook.save() # Return something return HttpResponse("*ok*")
def address_webhook(request, secret_key, ignored_key): ''' Process an inbound webhook from blockcypher ''' # Log webhook webhook = WebHook.log_webhook(request, WebHook.BLOCKCYPHER_ADDRESS_NOTIFICATION) assert secret_key == WEBHOOK_SECRET_KEY assert request.method == 'POST', 'Request has no post' blockcypher_id = request.META.get('HTTP_X_EVENTID') assert 'tx-confirmation' == request.META.get('HTTP_X_EVENTTYPE') payload = json.loads(request.body.decode()) address_subscription = AddressSubscription.objects.get(blockcypher_id=blockcypher_id) tx_hash = payload['hash'] num_confs = payload['confirmations'] double_spend = payload['double_spend'] satoshis_sent = payload['total'] fee_in_satoshis = payload['fees'] tx_event = get_object_or_None( OnChainTransaction, tx_hash=tx_hash, address_subscription=address_subscription, ) if tx_event: tx_event.num_confs = num_confs tx_event.double_spend = double_spend tx_event.save() else: tx_event = OnChainTransaction.objects.create( tx_hash=tx_hash, address_subscription=address_subscription, num_confs=num_confs, double_spend=double_spend, satoshis_sent=satoshis_sent, fee_in_satoshis=fee_in_satoshis, ) tx_event.send_email_notification() # Update logging webhook.address_subscription = address_subscription webhook.succeeded = True webhook.save() # Return something return HttpResponse("*ok*")
def process_bci_webhook(request, random_id): # Log webhook webhook = WebHook.log_webhook(request, WebHook.BCI_PAYMENT_FORWARDED) # parse webhook input_txn_hash = request.GET['input_transaction_hash'] destination_txn_hash = request.GET['transaction_hash'] satoshis = int(request.GET['value']) num_confirmations = int(request.GET['confirmations']) input_address = request.GET['input_address'] destination_address = request.GET['destination_address'] if input_address: forwarding_obj = get_object_or_None(ForwardingAddress, b58_address=input_address) if forwarding_obj: # Tie webhook to merchant. Optional. webhook.merchant = forwarding_obj.merchant webhook.save() # These defensive checks should always be true assert is_valid_btc_address(input_address), input_address assert is_valid_btc_address(destination_address), destination_address msg = '%s == %s' % (input_txn_hash, destination_txn_hash) assert input_txn_hash != destination_txn_hash, msg # process the transactions ForwardingAddress.handle_forwarding_txn( input_address=input_address, satoshis=satoshis, num_confirmations=num_confirmations, input_txn_hash=input_txn_hash, ) ForwardingAddress.handle_destination_txn( forwarding_address=input_address, destination_address=destination_address, satoshis=satoshis, num_confirmations=num_confirmations, destination_txn_hash=destination_txn_hash, ) if num_confirmations > 6: return HttpResponse("*ok*") else: return HttpResponse("Robot: please come back with more confirmations")
def address_webhook(request, secret_key, ignored_key): ''' Process an inbound webhook from blockcypher ''' # Log webhook webhook = WebHook.log_webhook(request, WebHook.BLOCKCYPHER_ADDRESS_NOTIFICATION) assert secret_key == WEBHOOK_SECRET_KEY assert request.method == 'POST', 'Request has no post' blockcypher_id = request.META.get('HTTP_X_EVENTID') assert 'tx-confirmation' == request.META.get('HTTP_X_EVENTTYPE') payload = json.loads(request.body.decode()) address_subscription = AddressSubscription.objects.get(blockcypher_id=blockcypher_id) tx_hash = payload['hash'] num_confs = payload['confirmations'] double_spend = payload['double_spend'] satoshis_sent = payload['total'] fee_in_satoshis = payload['fees'] tx_event = get_object_or_None( OnChainTransaction, tx_hash=tx_hash, address_subscription=address_subscription, ) if tx_event: tx_is_new = False tx_event.num_confs = num_confs tx_event.double_spend = double_spend tx_event.save() else: tx_is_new = True input_addresses = set() for input_entry in payload['inputs']: for address in input_entry.get('addresses', []): input_addresses.add(address) if address_subscription.b58_address in input_addresses: is_withdrawal = True else: is_withdrawal = False output_addresses = set() for output_entry in payload.get('outputs', []): for address in output_entry['addresses']: output_addresses.add(address) if address_subscription.b58_address in output_addresses: is_deposit = True else: is_deposit = False tx_event = OnChainTransaction.objects.create( tx_hash=tx_hash, address_subscription=address_subscription, num_confs=num_confs, double_spend=double_spend, satoshis_sent=satoshis_sent, fee_in_satoshis=fee_in_satoshis, is_deposit=is_deposit, is_withdrawal=is_withdrawal, ) # email sending logic # TODO: add logic for notify on deposit vs withdrawal # TODO: add safety check to prevent duplicate email sending if tx_event.is_subscribed(): if double_spend and (tx_is_new or not tx_event.double_spend): # We have the first reporting of a double-spend tx_event.send_double_spend_tx_notification() elif num_confs == 0 and tx_is_new: # First broadcast if tx_event.address_subscription.notify_on_broadcast: if tx_event.is_deposit and tx_event.address_subscription.notify_on_deposit: tx_event.send_unconfirmed_tx_email() elif tx_event.is_withdrawal and tx_event.address_subscription.notify_on_withdrawal: tx_event.send_unconfirmed_tx_email() elif num_confs == 6 and (tx_is_new or not tx_event.num_confs == num_confs): # Sixth confirm if tx_event.address_subscription.notify_on_sixth_confirm: if tx_event.is_deposit and tx_event.address_subscription.notify_on_deposit: tx_event.send_confirmed_tx_email() elif tx_event.is_withdrawal and tx_event.address_subscription.notify_on_withdrawal: tx_event.send_confirmed_tx_email() # Update logging webhook.address_subscription = address_subscription webhook.succeeded = True webhook.save() # Return something return HttpResponse("*ok*")
def address_webhook(request, secret_key, ignored_key): ''' Process an inbound webhook from blockcypher ''' # Log webhook webhook = WebHook.log_webhook(request, WebHook.BLOCKCYPHER_ADDRESS_NOTIFICATION) assert secret_key == WEBHOOK_SECRET_KEY assert request.method == 'POST', 'Request has no post' blockcypher_id = request.META.get('HTTP_X_EVENTID') assert 'tx-confirmation' == request.META.get('HTTP_X_EVENTTYPE') payload = json.loads(request.body.decode()) address_subscription = AddressSubscription.objects.get( blockcypher_id=blockcypher_id) tx_hash = payload['hash'] num_confs = payload['confirmations'] double_spend = payload['double_spend'] satoshis_sent = payload['total'] fee_in_satoshis = payload['fees'] tx_event = get_object_or_None( OnChainTransaction, tx_hash=tx_hash, address_subscription=address_subscription, ) if tx_event: tx_is_new = False tx_event.num_confs = num_confs tx_event.double_spend = double_spend tx_event.save() else: tx_is_new = True input_addresses = set() for input_entry in payload['inputs']: for address in input_entry.get('addresses', []): input_addresses.add(address) if address_subscription.b58_address in input_addresses: is_withdrawal = True else: is_withdrawal = False output_addresses = set() for output_entry in payload.get('outputs', []): for address in output_entry['addresses']: output_addresses.add(address) if address_subscription.b58_address in output_addresses: is_deposit = True else: is_deposit = False tx_event = OnChainTransaction.objects.create( tx_hash=tx_hash, address_subscription=address_subscription, num_confs=num_confs, double_spend=double_spend, satoshis_sent=satoshis_sent, fee_in_satoshis=fee_in_satoshis, is_deposit=is_deposit, is_withdrawal=is_withdrawal, ) # email sending logic # TODO: add logic for notify on deposit vs withdrawal # TODO: add safety check to prevent duplicate email sending if tx_event.is_subscribed(): if double_spend and (tx_is_new or not tx_event.double_spend): # We have the first reporting of a double-spend tx_event.send_double_spend_tx_notification() elif num_confs == 0 and tx_is_new: # First broadcast if tx_event.address_subscription.notify_on_broadcast: if tx_event.is_deposit and tx_event.address_subscription.notify_on_deposit: tx_event.send_unconfirmed_tx_email() elif tx_event.is_withdrawal and tx_event.address_subscription.notify_on_withdrawal: tx_event.send_unconfirmed_tx_email() elif num_confs == 6 and (tx_is_new or not tx_event.num_confs == num_confs): # Sixth confirm if tx_event.address_subscription.notify_on_sixth_confirm: if tx_event.is_deposit and tx_event.address_subscription.notify_on_deposit: tx_event.send_confirmed_tx_email() elif tx_event.is_withdrawal and tx_event.address_subscription.notify_on_withdrawal: tx_event.send_confirmed_tx_email() # Update logging webhook.address_subscription = address_subscription webhook.succeeded = True webhook.save() # Return something return HttpResponse("*ok*")
def address_webhook(request, secret_key, ignored_key): ''' Process an inbound webhook from blockcypher ''' # Log webhook webhook = WebHook.log_webhook(request, WebHook.BLOCKCYPHER_ADDRESS_NOTIFICATION) assert secret_key == WEBHOOK_SECRET_KEY assert request.method == 'POST', 'Request has no post' blockcypher_id = request.META.get('HTTP_X_EVENTID') assert 'tx-confirmation' == request.META.get('HTTP_X_EVENTTYPE') payload = json.loads(request.body.decode()) address_subscription = AddressSubscription.objects.get( blockcypher_id=blockcypher_id) tx_hash = payload['hash'] num_confs = payload['confirmations'] double_spend = payload['double_spend'] satoshis_sent = payload['total'] fee_in_satoshis = payload['fees'] tx_event = get_object_or_None( OnChainTransaction, tx_hash=tx_hash, address_subscription=address_subscription, ) if tx_event: tx_is_new = False tx_event.num_confs = num_confs tx_event.double_spend = double_spend tx_event.save() else: tx_is_new = True input_addresses = set() for input_entry in payload['inputs']: for address in input_entry.get('addresses', []): input_addresses.add(address) if address_subscription.b58_address in input_addresses: is_withdrawal = True else: is_withdrawal = False output_addresses = set() for output_entry in payload.get('outputs', []): for address in output_entry['addresses']: output_addresses.add(address) if address_subscription.b58_address in output_addresses: is_deposit = True else: is_deposit = False tx_event = OnChainTransaction.objects.create( tx_hash=tx_hash, address_subscription=address_subscription, num_confs=num_confs, double_spend=double_spend, satoshis_sent=satoshis_sent, fee_in_satoshis=fee_in_satoshis, is_deposit=is_deposit, is_withdrawal=is_withdrawal, ) # email sending logic # TODO: add logic for notify on deposit vs withdrawal # TODO: add safety check to prevent duplicate email sending if tx_event.address_subscription.unsubscribed_at or tx_event.address_subscription.disabled_at: # unsubscribe from webhooks going forward try: unsub_result = unsubscribe_from_webhook( webhook_id=tx_event.address_subscription.blockcypher_id, api_key=BLOCKCYPHER_API_KEY, coin_symbol=tx_event.address_subscription.coin_symbol, ) assert unsub_result is True, unsub_result except Exception: # there was a problem unsubscribing # notify using sentry but still return the webhook to blockcypher client.captureException() elif tx_event.address_subscription.auth_user.email_verified: # make sure we haven't contacted too many times (and unsub if so) earliest_dt = now() - timedelta(days=3) recent_emails_sent = SentEmail.objects.filter( address_subscription=tx_event.address_subscription, sent_at__gt=earliest_dt, ).count() if recent_emails_sent > 100: # too many emails, unsubscribe tx_event.address_subscription.admin_unsubscribe_subscription() client.captureMessage('TX Event %s unsubscribed' % tx_event.id) # TODO: notify user they've been unsubscribed else: # proceed with normal email sending if double_spend and (tx_is_new or not tx_event.double_spend): # We have the first reporting of a double-spend tx_event.send_double_spend_tx_notification() elif num_confs == 0 and tx_is_new: # First broadcast if tx_event.address_subscription.notify_on_broadcast: if tx_event.is_deposit and tx_event.address_subscription.notify_on_deposit: tx_event.send_unconfirmed_tx_email() elif tx_event.is_withdrawal and tx_event.address_subscription.notify_on_withdrawal: tx_event.send_unconfirmed_tx_email() elif num_confs == 6: # Sixth confirm if tx_event.address_subscription.notify_on_sixth_confirm: if tx_event.is_deposit and tx_event.address_subscription.notify_on_deposit: tx_event.send_confirmed_tx_email() elif tx_event.is_withdrawal and tx_event.address_subscription.notify_on_withdrawal: tx_event.send_confirmed_tx_email() else: # active subscription with unverfied email (can't contact) # TODO: add unsub if orig subscription is > X days old # eventually these could pile up pass # Update logging webhook.address_subscription = address_subscription webhook.succeeded = True webhook.save() # Return something return HttpResponse("*ok*")
def address_webhook(request, secret_key, ignored_key): ''' Process an inbound webhook from blockcypher ''' # Log webhook webhook = WebHook.log_webhook(request, WebHook.BLOCKCYPHER_ADDRESS_NOTIFICATION) assert secret_key == WEBHOOK_SECRET_KEY assert request.method == 'POST', 'Request has no post' blockcypher_id = request.META.get('HTTP_X_EVENTID') assert 'tx-confirmation' == request.META.get('HTTP_X_EVENTTYPE') payload = json.loads(request.body.decode()) address_subscription = AddressSubscription.objects.get(blockcypher_id=blockcypher_id) tx_hash = payload['hash'] num_confs = payload['confirmations'] double_spend = payload['double_spend'] satoshis_sent = payload['total'] fee_in_satoshis = payload['fees'] tx_event = get_object_or_None( OnChainTransaction, tx_hash=tx_hash, address_subscription=address_subscription, ) if tx_event: tx_is_new = False tx_event.num_confs = num_confs tx_event.double_spend = double_spend tx_event.save() else: tx_is_new = True input_addresses = set() for input_entry in payload['inputs']: for address in input_entry.get('addresses', []): input_addresses.add(address) if address_subscription.b58_address in input_addresses: is_withdrawal = True else: is_withdrawal = False output_addresses = set() for output_entry in payload.get('outputs', []): for address in output_entry['addresses']: output_addresses.add(address) if address_subscription.b58_address in output_addresses: is_deposit = True else: is_deposit = False tx_event = OnChainTransaction.objects.create( tx_hash=tx_hash, address_subscription=address_subscription, num_confs=num_confs, double_spend=double_spend, satoshis_sent=satoshis_sent, fee_in_satoshis=fee_in_satoshis, is_deposit=is_deposit, is_withdrawal=is_withdrawal, ) # email sending logic # TODO: add logic for notify on deposit vs withdrawal # TODO: add safety check to prevent duplicate email sending if tx_event.address_subscription.unsubscribed_at or tx_event.address_subscription.disabled_at: # unsubscribe from webhooks going forward try: unsub_result = unsubscribe_from_webhook( webhook_id=tx_event.address_subscription.blockcypher_id, api_key=BLOCKCYPHER_API_KEY, coin_symbol=tx_event.address_subscription.coin_symbol, ) assert unsub_result is True, unsub_result except Exception: # there was a problem unsubscribing # notify using sentry but still return the webhook to blockcypher client.captureException() elif tx_event.address_subscription.auth_user.email_verified: # make sure we haven't contacted too many times (and unsub if so) earliest_dt = now() - timedelta(days=3) recent_emails_sent = SentEmail.objects.filter( address_subscription=tx_event.address_subscription, sent_at__gt=earliest_dt, ).count() if recent_emails_sent > 100: # too many emails, unsubscribe tx_event.address_subscription.admin_unsubscribe_subscription() client.captureMessage( 'TX Event %s unsubscribed' % tx_event.id, data={ 'tx_event': tx_event.id, 'address_subscription': tx_event.address_subscription, 'recent_emails_sent': recent_emails_sent, 'webhook': webhook.id, }, ) # TODO: notify user they've been unsubscribed else: # proceed with normal email sending if double_spend and (tx_is_new or not tx_event.double_spend): # We have the first reporting of a double-spend tx_event.send_double_spend_tx_notification() elif num_confs == 0 and tx_is_new: # First broadcast if tx_event.address_subscription.notify_on_broadcast: if tx_event.is_deposit and tx_event.address_subscription.notify_on_deposit: tx_event.send_unconfirmed_tx_email() elif tx_event.is_withdrawal and tx_event.address_subscription.notify_on_withdrawal: tx_event.send_unconfirmed_tx_email() elif num_confs == 6: # Sixth confirm if tx_event.address_subscription.notify_on_sixth_confirm: if tx_event.is_deposit and tx_event.address_subscription.notify_on_deposit: tx_event.send_confirmed_tx_email() elif tx_event.is_withdrawal and tx_event.address_subscription.notify_on_withdrawal: tx_event.send_confirmed_tx_email() else: # active subscription with unverfied email (can't contact) # TODO: add unsub if orig subscription is > X days old # eventually these could pile up pass # Update logging webhook.address_subscription = address_subscription webhook.succeeded = True webhook.save() # Return something return HttpResponse("*ok*")