diff --git a/helpers.py b/helpers.py
index d3c9f93..a880e24 100644
--- a/helpers.py
+++ b/helpers.py
@@ -4,6 +4,7 @@ import secrets
from typing import Any, Optional, Tuple
import secp256k1
+from bech32 import bech32_decode, convertbits
from cffi import FFI
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
@@ -80,3 +81,21 @@ def order_from_json(s: str) -> Tuple[Optional[Any], Optional[str]]:
return (order, s) if (type(order) is dict) and "items" in order else (None, s)
except ValueError:
return None, s
+
+
+def normalize_public_key(pubkey: str) -> str:
+ if pubkey.startswith("npub1"):
+ _, decoded_data = bech32_decode(pubkey)
+ if not decoded_data:
+ raise ValueError("Public Key is not valid npub")
+
+ decoded_data_bits = convertbits(decoded_data, 5, 8, False)
+ if not decoded_data_bits:
+ raise ValueError("Public Key is not valid npub")
+ return bytes(decoded_data_bits).hex()
+
+ # check if valid hex
+ if len(pubkey) != 64:
+ raise ValueError("Public Key is not valid hex")
+ int(pubkey, 16)
+ return pubkey
diff --git a/services.py b/services.py
index 89b4859..57bb86a 100644
--- a/services.py
+++ b/services.py
@@ -352,7 +352,7 @@ async def _handle_new_order(order: PartialOrder) -> Optional[str]:
return None
-async def _handle_new_customer(event, merchant):
+async def _handle_new_customer(event, merchant: Merchant):
await create_customer(
merchant.id, Customer(merchant_id=merchant.id, public_key=event.pubkey)
)
diff --git a/static/components/direct-messages/direct-messages.html b/static/components/direct-messages/direct-messages.html
index ab7eee1..a1f8662 100644
--- a/static/components/direct-messages/direct-messages.html
+++ b/static/components/direct-messages/direct-messages.html
@@ -26,14 +26,31 @@
-
-
+
+
+
+
+
+
+
+
+ Add a public key to chat with
+
+
+
+
+
@@ -76,4 +93,30 @@
+
+
+
+
+
+
+ Add
+ Cancel
+
+
+
+
+
diff --git a/static/components/direct-messages/direct-messages.js b/static/components/direct-messages/direct-messages.js
index 39bc7d3..d034fe1 100644
--- a/static/components/direct-messages/direct-messages.js
+++ b/static/components/direct-messages/direct-messages.js
@@ -2,7 +2,7 @@ async function directMessages(path) {
const template = await loadTemplateAsync(path)
Vue.component('direct-messages', {
name: 'direct-messages',
- props: ['active-chat-customer', 'adminkey', 'inkey'],
+ props: ['active-chat-customer', 'merchant-id', 'adminkey', 'inkey'],
template,
watch: {
@@ -19,7 +19,9 @@ async function directMessages(path) {
unreadMessages: 0,
activePublicKey: null,
messages: [],
- newMessage: ''
+ newMessage: '',
+ showAddPublicKey: false,
+ newPublicKey: null
}
},
methods: {
@@ -56,7 +58,7 @@ async function directMessages(path) {
try {
const {data} = await LNbits.api.request(
'GET',
- '/nostrmarket/api/v1/customers',
+ '/nostrmarket/api/v1/customer',
this.inkey
)
this.customers = data
@@ -83,6 +85,27 @@ async function directMessages(path) {
LNbits.utils.notifyApiError(error)
}
},
+ addPublicKey: async function(){
+ try {
+ const {data} = await LNbits.api.request(
+ 'POST',
+ '/nostrmarket/api/v1/customer',
+ this.adminkey,
+ {
+ public_key: this.newPublicKey,
+ merchant_id: this.merchantId,
+ unread_messages: 0
+ }
+ )
+ this.newPublicKey = null
+ this.activePublicKey = data.public_key
+ await this.selectActiveCustomer()
+ } catch (error) {
+ LNbits.utils.notifyApiError(error)
+ } finally {
+ this.showAddPublicKey = false
+ }
+ },
handleNewMessage: async function (data) {
if (data.customerPubkey === this.activePublicKey) {
await this.getDirectMessages(this.activePublicKey)
diff --git a/static/components/order-list/order-list.js b/static/components/order-list/order-list.js
index f1c883d..c4539b3 100644
--- a/static/components/order-list/order-list.js
+++ b/static/components/order-list/order-list.js
@@ -227,7 +227,7 @@ async function orderList(path) {
try {
const {data} = await LNbits.api.request(
'GET',
- '/nostrmarket/api/v1/customers',
+ '/nostrmarket/api/v1/customer',
this.inkey
)
this.customers = data
diff --git a/templates/nostrmarket/index.html b/templates/nostrmarket/index.html
index 89e01ba..603cf6b 100644
--- a/templates/nostrmarket/index.html
+++ b/templates/nostrmarket/index.html
@@ -167,6 +167,7 @@
:inkey="g.user.wallets[0].inkey"
:adminkey="g.user.wallets[0].adminkey"
:active-chat-customer="activeChatCustomer"
+ :merchant-id="merchant.id"
@customer-selected="filterOrdersForCustomer"
>
diff --git a/views_api.py b/views_api.py
index 0fa8317..daad678 100644
--- a/views_api.py
+++ b/views_api.py
@@ -13,10 +13,12 @@ from lnbits.decorators import (
require_admin_key,
require_invoice_key,
)
+from lnbits.extensions.nostrmarket.helpers import normalize_public_key
from lnbits.utils.exchange_rates import currencies
from . import nostr_client, nostrmarket_ext, scheduled_tasks
from .crud import (
+ create_customer,
create_direct_message,
create_merchant,
create_product,
@@ -31,6 +33,7 @@ from .crud import (
delete_product,
delete_stall,
delete_zone,
+ get_customer,
get_customers,
get_direct_messages,
get_merchant_by_pubkey,
@@ -796,7 +799,7 @@ async def api_create_message(
######################################## CUSTOMERS ########################################
-@nostrmarket_ext.get("/api/v1/customers")
+@nostrmarket_ext.get("/api/v1/customer")
async def api_get_customers(
wallet: WalletTypeInfo = Depends(get_key_type),
) -> List[Customer]:
@@ -818,6 +821,41 @@ async def api_get_customers(
)
+@nostrmarket_ext.post("/api/v1/customer")
+async def api_createcustomer(
+ data: Customer,
+ wallet: WalletTypeInfo = Depends(require_admin_key),
+) -> Customer:
+
+ try:
+ pubkey = normalize_public_key(data.public_key)
+
+ merchant = await get_merchant_for_user(wallet.wallet.user)
+ assert merchant, "A merchant does not exists for this user"
+ assert merchant.id == data.merchant_id, "Invalid merchant id for user"
+
+ existing_customer = await get_customer(merchant.id, pubkey)
+ assert existing_customer == None, "This public key already exists"
+
+ customer = await create_customer(
+ merchant.id, Customer(merchant_id=merchant.id, public_key=pubkey)
+ )
+ await nostr_client.subscribe_to_user_profile(pubkey, 0)
+
+ return customer
+ except (ValueError, AssertionError) as ex:
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail=str(ex),
+ )
+ except Exception as ex:
+ logger.warning(ex)
+ raise HTTPException(
+ status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
+ detail="Cannot create customer",
+ )
+
+
######################################## OTHER ########################################