Beispiel #1
0
def ifttt_bunq_payment(internal, draft):
    """ Execute a draft, internal or external payment """
    config = bunq.retrieve_config()
    data = request.get_json()
    print("[action_payment] input: {}".format(json.dumps(data)))

    errmsg = None
    if not internal and not draft and not util.get_external_payment_enabled():
        errmsg = "external payments disabled"
    if "actionFields" not in data:
        errmsg = "missing actionFields"

    if errmsg:
        print("[action_payment] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    # get the payment message
    fields = data["actionFields"]
    msg = create_payment_message(internal, fields, config)
    if "errors" in msg or "data" in msg:  # error or test payment
        return json.dumps(msg), 400 if "errors" in msg else 200

    # find the source account id
    source_accid, enabled = check_source_account(internal, draft, config,
                                                 fields["source_account"])
    if source_accid is None:
        errmsg = "unknown source account: " + fields["source_account"]
    if not enabled:
        errmsg = "Payment type not enabled for account: "+\
                 fields["source_account"]

    if errmsg:
        print("[action_payment] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    # execute the payment
    if draft:
        msg = {"number_of_required_accepts": 1, "entries": [msg]}
        result = bunq.post(
            "v1/user/{}/monetary-account/{}/draft-payment".format(
                config["user_id"], source_accid), msg)
    else:
        result = bunq.post(
            "v1/user/{}/monetary-account/{}/payment".format(
                config["user_id"], source_accid), msg)
    print(result)
    if "Error" in result:
        return json.dumps({
            "errors": [{
                "status": "SKIP",
                "message": result["Error"][0]["error_description"]
            }]
        }), 400

    return json.dumps(
        {"data": [{
            "id": str(result["Response"][0]["Id"]["id"])
        }]})
Beispiel #2
0
def put_callbacks(user_id, account_id, new_notifications):
    data = {
         "notification_filters": new_notifications
    }
    method = (f"v1/user/{user_id}/monetary-account/{account_id}/" +
               "notification-filter-url")
    bunq.post(method, data)
Beispiel #3
0
def transfer(row, source_name, target_name, amount, keep, description):
    accounts = collect_accounts(row)
    source = find_account(accounts, source_name)
    if not source:
        return {"error": f"No account matches source {source_name}"}
    target = find_account(accounts, target_name)
    if not target:
        return {"error": f"No account matches target {target_name}"}

    source_amount = Decimal(source["value"])
    if amount == "all":
        amount_dec = source_amount
        if amount_dec <= 0:
            return {"error": f"There is no money in the source account"}
    else:
        amount_dec = Decimal(amount)

    if source_amount - amount_dec < keep:
        amount_dec = source_amount - keep
        if amount_dec <= 0:
            return {"error": f"Less than 'keep' money in source account"}

    if Decimal(source["value"]) < amount_dec:
        return {"error": f"There is not enough money in the source account"}

    # Move balance to target account
    method = (f"v1/user/{source['user_id']}/monetary-account/" +
              f"{source['account_id']}/payment")
    data = {
        "amount": {
            "value": str(amount_dec),
            "currency": source["currency"]
        },
        "counterparty_alias": {
            "type": "IBAN",
            "value": target["iban"],
            "name": target["name"]
        },
        "description": description
    }
    bunq.post(row, method, data)
    return {
        "success":
        "success",
        "message":
        f"Transferred {amount_dec} {source['currency']} " +
        f"from {source['iban']} to {target['iban']}"
    }
Beispiel #4
0
def request_inquiry():
    """ Execute a request inquiry action """
    data = request.get_json()
    print("[request_inquiry] input: {}".format(json.dumps(data)))

    errmsg = None
    if "actionFields" not in data:
        errmsg = "missing actionFields"
    else:
        fields = data["actionFields"]
        expected_fields = ["amount", "account", "phone_email_iban"]
        for field in expected_fields:
            if field not in fields:
                errmsg = "missing field: " + field

    if errmsg:
        print("[request_inquiry] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    fields["account"] = fields["account"].replace(" ", "")

    # the account NL42BUNQ0123456789 is used for test payments
    if fields["account"] == "NL42BUNQ0123456789":
        return json.dumps({"data": [{"id": uuid.uuid4().hex}]})

    accountid = None
    for acc in util.get_bunq_accounts("PaymentRequest"):
        if acc["iban"] == fields["account"]:
            accountid = acc["id"]
    if accountid is None:
        errmsg = "unknown account: " + fields["account"]
        print("[request_inquiry] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    # check amount
    try:
        amount = float(fields["amount"])
    except ValueError:
        amount = -1
    if amount <= 0:
        errmsg = "only positive amounts allowed: " + fields["amount"]
        print("[action_payment] ERROR: " + errmsg)
        return {"errors": [{"status": "SKIP", "message": errmsg}]}

    # check phone or email
    bmvalue = fields["phone_email_iban"].replace(" ", "")
    if "@" in bmvalue:
        bmtype = "EMAIL"
    elif bmvalue[:1] == "+" and bmvalue[1:].isdecimal():
        bmtype = "PHONE_NUMBER"
    elif bmvalue[:2].isalpha() and bmvalue[2:4].isdecimal():
        bmtype = "IBAN"
    else:
        errmsg = "Unrecognized as email, phone or iban: " + bmvalue
        print("[request_inquiry] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    description = fields["description"] if "description" in fields else ""
    msg = {
        "amount_inquired": {
            "value": "{:.2f}".format(amount),
            "currency": "EUR",
        },
        "counterparty_alias": {
            "type": bmtype,
            "name": bmvalue,
            "value": bmvalue
        },
        "description": description,
        "allow_bunqme": True,
    }
    print(json.dumps(msg))

    config = bunq.retrieve_config()
    data = bunq.post("v1/user/{}/monetary-account/{}/request-inquiry".format(\
                     config["user_id"], accountid), msg, config)
    print(data)
    if "Error" in data:
        return json.dumps({
            "errors": [{
                "status": "SKIP",
                "message": data["Error"][0]["error_description"]
            }]
        }), 400

    return json.dumps({"data": [{"id": uuid.uuid4().hex}]})
Beispiel #5
0
if not source:
    print(f"No account matches source {args.source_name}")
    sys.exit(1)
target = find_account(accounts, args.target_name)
if not target:
    print(f"No account matches target {args.target_name}")
    sys.exit(1)

if Decimal(source["value"]) <= 0:
    print("There is no money in the source account")
    sys.exit(1)

# Move balance to target account
print(f"Sending {source['value']} {source['currency']} from " +
      f"{source['iban']} to {target['iban']}...")
method = (f"v1/user/{source['user_id']}/monetary-account/" +
          f"{source['account_id']}/payment")
data = {
    "amount": {
        "value": source["value"],
        "currency": source["currency"]
    },
    "counterparty_alias": {
        "type": "IBAN",
        "value": target["iban"],
        "name": target["name"]
    },
    "description": "Flushing account"
}
bunq.post(method, data)
Beispiel #6
0
                removed_notification = True
            else:
                new_notifications.append({
                    "category": nf["category"],
                    "notification_target": nf["notification_target"]
                })

    if not removed_notification:
        print("Adding callback...")
        new_notifications.append({
            "category": args.toggle_category,
            "notification_target": args.toggle_url,
        })
    return new_notifications


bunq_user_id = bunq_api.get_user_id(args.bunq_user_name)
if args.bunq_account_name:
    bunq_account_id = bunq_api.get_account_id(bunq_user_id,
                                              args.bunq_account_name)
    method = (f"v1/user/{bunq_user_id}/monetary-account/"
              f"{bunq_account_id}/notification-filter-url")
    old_nfs = bunq.get(method)
    new_nfs = update_notifications(old_nfs)
    bunq.post(method, {"notification_filters": new_nfs})
else:
    method = f"v1/user/{bunq_user_id}/notification-filter-url"
    old_nfs = bunq.get(method)
    new_nfs = update_notifications(old_nfs)
    bunq.post(method, {"notification_filters": new_nfs})
Beispiel #7
0
def target_balance_internal():
    """ Execute a target balance internal action """
    data = request.get_json()
    print("[target_balance_internal] input: {}".format(json.dumps(data)))

    if "actionFields" not in data:
        errmsg = "missing actionFields"
        print("[target_balance_internal] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    fields = data["actionFields"]
    errmsg = check_fields(True, fields)
    if errmsg:
        print("[target_balance_internal] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    # the account NL42BUNQ0123456789 is used for test payments
    if fields["account"] == "NL42BUNQ0123456789":
        return json.dumps({"data": [{"id": uuid.uuid4().hex}]})

    # retrieve balance
    config = bunq.retrieve_config()
    if fields["payment_type"] == "DIRECT":
        balance = get_balance(config, fields["account"],
                              fields["other_account"])
        if isinstance(balance, tuple):
            balance, balance2 = balance
            transfer_amount = fields["amount"] - balance
            if transfer_amount > balance2:
                transfer_amount = balance2
    else:
        balance = get_balance(config, fields["account"])
        if isinstance(balance, float):
            transfer_amount = fields["amount"] - balance

    if isinstance(balance, str):
        errmsg = balance
        print("[target_balance_internal] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    # construct payment message
    if "{:.2f}".format(fields["amount"]) == "0.00":
        errmsg = "No transfer needed, balance already ok"
        print("[target_balance_internal] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    if transfer_amount > 0 and "top up" in fields["direction"]:
        paymentmsg = {
            "amount": {
                "value": "{:.2f}".format(transfer_amount),
                "currency": "EUR"
            },
            "counterparty_alias": {
                "type": "IBAN",
                "value": fields["account"],
                "name": "x"
            },
            "description": fields["description"]
        }
        account = fields["other_account"]
    elif transfer_amount < 0 and "skim" in fields["direction"]:
        paymentmsg = {
            "amount": {
                "value": "{:.2f}".format(-transfer_amount),
                "currency": "EUR"
            },
            "counterparty_alias": {
                "type": "IBAN",
                "value": fields["other_account"],
                "name": "x"
            },
            "description": fields["description"]
        }
        account = fields["account"]
    else:
        errmsg = "No transfer needed, balance already ok"
        print("[target_balance_internal] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    print(paymentmsg)

    # get id and check permissions
    if fields["payment_type"] == "DIRECT":
        accid, enabled = payment.check_source_account(True, False, config,
                                                      account)
    else:
        accid, enabled = payment.check_source_account(False, True, config,
                                                      account)
    if accid is None:
        errmsg = "unknown account: " + account
    if not enabled:
        errmsg = "Payment type not enabled for account: " + account

    if errmsg:
        print("[target_balance_internal] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    # execute the payment
    if fields["payment_type"] == "DIRECT":
        result = bunq.post(
            "v1/user/{}/monetary-account/{}/payment".format(
                config["user_id"], accid), paymentmsg)
    else:
        paymentmsg = {"number_of_required_accepts": 1, "entries": [paymentmsg]}
        result = bunq.post(
            "v1/user/{}/monetary-account/{}/draft-payment".format(
                config["user_id"], accid), paymentmsg)
    print(result)
    if "Error" in result:
        return json.dumps({
            "errors": [{
                "status": "SKIP",
                "message": result["Error"][0]["error_description"]
            }]
        }), 400

    return json.dumps(
        {"data": [{
            "id": str(result["Response"][0]["Id"]["id"])
        }]})
Beispiel #8
0
def target_balance_external():
    """ Execute a target balance external action """
    data = request.get_json()
    print("[target_balance_external] input: {}".format(json.dumps(data)))

    if "actionFields" not in data:
        errmsg = "missing actionFields"
        print("[target_balance_external] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    fields = data["actionFields"]
    errmsg = check_fields(False, fields)
    if errmsg:
        print("[target_balance_external] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    # the account NL42BUNQ0123456789 is used for test payments
    if fields["account"] == "NL42BUNQ0123456789":
        return json.dumps({"data": [{"id": uuid.uuid4().hex}]})

    # retrieve balance
    config = bunq.retrieve_config()
    balance = get_balance(config, fields["account"])
    if isinstance(balance, str):
        errmsg = balance
        print("[target_balance_external] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    transfer_amount = fields["amount"] - balance

    # check for zero transfer
    if "{:.2f}".format(fields["amount"]) == "0.00":
        errmsg = "No transfer needed, balance already ok"
        print("[target_balance_external] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    # get account id and check permission
    if transfer_amount > 0:
        accid = None
        for acc in config["accounts"]:
            if acc["iban"] == fields["account"]:
                accid = acc["id"]

        enabled = False
        if "permissions" in config:
            if fields["account"] in config["permissions"]:
                if "PaymentRequest" in config["permissions"]\
                                             [fields["account"]]:
                    enabled = config["permissions"][fields["account"]]\
                                    ["PaymentRequest"]
    else:
        accid, enabled = payment.check_source_account(False, True, config,
                                                      fields["account"])

    if accid is None:
        errmsg = "unknown account: " + fields["account"]
    if not enabled:
        errmsg = "Not permitted for account: " + fields["account"]

    if errmsg:
        print("[target_balance_external] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
               , 400

    # send request / execute payment
    if transfer_amount > 0 and "top up" in fields["direction"]:

        bmvalue = fields["request_phone_email_iban"].replace(" ", "")
        if "@" in bmvalue:
            bmtype = "EMAIL"
        elif bmvalue[:1] == "+" and bmvalue[1:].isdecimal():
            bmtype = "PHONE_NUMBER"
        elif bmvalue[:2].isalpha() and bmvalue[2:4].isdecimal():
            bmtype = "IBAN"
        else:
            errmsg = "Unrecognized as email, phone or iban: " + bmvalue
            print("[request_inquiry] ERROR: " + errmsg)
            return json.dumps({"errors": [{"status": "SKIP", "message":\
                   errmsg}]}), 400

        msg = {
            "amount_inquired": {
                "value": "{:.2f}".format(transfer_amount),
                "currency": "EUR",
            },
            "counterparty_alias": {
                "type": bmtype,
                "name": bmvalue,
                "value": bmvalue
            },
            "description": fields["request_description"],
            "allow_bunqme": True,
        }
        print(json.dumps(msg))

        config = bunq.retrieve_config()
        result = bunq.post("v1/user/{}/monetary-account/{}/request-inquiry"\
                           .format(config["user_id"], accid), msg, config)

    elif transfer_amount < 0 and "skim" in fields["direction"]:
        paymentmsg = {
            "amount": {
                "value": "{:.2f}".format(-transfer_amount),
                "currency": "EUR"
            },
            "counterparty_alias": {
                "type": "IBAN",
                "value": fields["payment_account"],
                "name": fields["payment_name"]
            },
            "description": fields["payment_description"]
        }
        print(paymentmsg)
        paymentmsg = {"number_of_required_accepts": 1, "entries": [paymentmsg]}
        result = bunq.post(
            "v1/user/{}/monetary-account/{}/draft-payment".format(
                config["user_id"], accid), paymentmsg)

    else:
        errmsg = "No transfer needed, balance already ok"
        print("[target_balance_external] ERROR: " + errmsg)
        return json.dumps({"errors": [{"status": "SKIP", "message": errmsg}]})\
                , 400

    print(result)
    if "Error" in result:
        return json.dumps({
            "errors": [{
                "status": "SKIP",
                "message": result["Error"][0]["error_description"]
            }]
        }), 400

    return json.dumps(
        {"data": [{
            "id": str(result["Response"][0]["Id"]["id"])
        }]})