Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/bcr-ebill-api/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ pub const COMPANY_LOGO_FILE_FIELD: &str = "logo_file";
pub const COMPANY_PROOF_OF_REGISTRATION_FILE_FIELD: &str = "proof_of_registration_file";
pub const IDENTITY_PROFILE_PICTURE_FILE_FIELD: &str = "profile_picture_file";
pub const IDENTITY_DOCUMENT_FILE_FIELD: &str = "identity_document_file";
pub const SAVE_SEED_PHRASE_NOTIFICATION_KEY: &str = "save_seed_phrase";
pub const SAVE_SEED_PHRASE_NOTIFICATION_REFERENCE_ID: &str = "seed_phrase";
30 changes: 29 additions & 1 deletion crates/bcr-ebill-api/src/service/bill_service/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use bcr_ebill_core::{
BcrKeys,
btc::{calculate_tweak_hash_for_payment_request, get_address_to_pay},
},
event::{CompanyChainEvent, IdentityChainEvent},
event::{ActionType, BillEventType, CompanyChainEvent, IdentityChainEvent},
},
};
use log::error;
Expand Down Expand Up @@ -655,6 +655,34 @@ impl BillService {
self.validate_and_add_block(&bill_id, blockchain, block.clone())
.await?;

match bill_action {
BillAction::Sell(_) => {
self.transport_service
.notification_transport()
.create_local_bill_notification(
&signer_public_data.node_id(),
&bill_id,
BillEventType::BillSold,
Some(ActionType::CheckBill),
Some(bill.sum.clone()),
)
.await?;
}
BillAction::Recourse(_) => {
self.transport_service
.notification_transport()
.create_local_bill_notification(
&signer_public_data.node_id(),
&bill_id,
BillEventType::BillRecoursePaid,
Some(ActionType::CheckBill),
Some(bill.sum.clone()),
)
.await?;
}
_ => {}
}

self.add_identity_and_company_chain_blocks_for_signed_bill_action(
signer_public_data,
&bill_id,
Expand Down
43 changes: 22 additions & 21 deletions crates/bcr-ebill-api/src/service/bill_service/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ use bcr_ebill_core::{
blockchain::{
Blockchain,
bill::{
BillBlockchain, BillOpCode, BitcreditBill, OfferToSellWaitingForPayment,
RecourseWaitingForPayment,
BillOpCode, BitcreditBill, OfferToSellWaitingForPayment, RecourseWaitingForPayment,
block::BillRequestToPayBlockData,
participant::{BillAnonParticipant, BillIdentParticipant, BillParticipant},
},
identity::IdentityType,
},
crypto::BcrKeys,
event::BillChainEvent,
event::{ActionType, BillEventType},
},
};
use log::{debug, info};
Expand Down Expand Up @@ -89,9 +88,7 @@ impl BillService {
self.store.invalidate_bill_in_cache(bill_id).await?;
// the bill is paid now - trigger notification
if let PaymentState::PaidConfirmed(_) = payment_state
&& let Err(e) = self
.trigger_is_paid_notification(identity, &chain, &bill_keys, &bill)
.await
&& let Err(e) = self.trigger_is_paid_notification(&bill).await
{
log::error!("Could not send is-paid notification for {bill_id}: {e}");
}
Expand All @@ -108,22 +105,26 @@ impl BillService {
Ok(())
}

async fn trigger_is_paid_notification(
&self,
identity: &Identity,
blockchain: &BillBlockchain,
bill_keys: &BcrKeys,
last_version_bill: &BitcreditBill,
) -> Result<()> {
let chain_event = BillChainEvent::new(
last_version_bill,
blockchain,
bill_keys,
true,
&identity.node_id,
)?;
async fn trigger_is_paid_notification(&self, last_version_bill: &BitcreditBill) -> Result<()> {
let holder_node_id = last_version_bill
.endorsee
.as_ref()
.map(|e| e.node_id())
.unwrap_or_else(|| last_version_bill.payee.node_id());
log::debug!(
"BillPaid notification: bill_id={} recipient={}",
last_version_bill.id,
holder_node_id
);
self.transport_service
.send_bill_is_paid_event(&chain_event)
.notification_transport()
.create_local_bill_notification(
&holder_node_id,
&last_version_bill.id,
BillEventType::BillPaid,
Some(ActionType::CheckBill),
Some(last_version_bill.sum.clone()),
)
.await?;
Ok(())
}
Expand Down
16 changes: 15 additions & 1 deletion crates/bcr-ebill-api/src/service/bill_service/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
service::{
company_service::tests::{get_valid_company_chain, get_valid_identity_chain},
contact_service::tests::get_baseline_contact,
transport_service::MockTransportServiceApi,
transport_service::{MockNotificationTransportServiceApi, MockTransportServiceApi},
},
tests::tests::{
MockBillChainStoreApiMock, MockBillStoreApiMock, MockCompanyChainStoreApiMock,
Expand Down Expand Up @@ -293,6 +293,20 @@ pub fn get_service(mut ctx: MockBillContext) -> BillService {
ctx.transport_service
.expect_publish_file_metadata()
.returning(|_, _, _, _, _| Ok(()));
let mut default_notification = MockNotificationTransportServiceApi::new();
default_notification
.expect_get_active_bill_notification()
.returning(|_| None);
default_notification
.expect_create_local_bill_notification()
.returning(|_, _, _, _, _| Ok(()));
default_notification
.expect_create_general_notification()
.returning(|_, _, _, _| Ok(()));
ctx.transport_service
.expect_notification_transport()
.times(0..)
.return_const(Arc::new(default_notification));
BillService::new(
Arc::new(ctx.bill_store),
Arc::new(ctx.bill_blockchain_store),
Expand Down
17 changes: 16 additions & 1 deletion crates/bcr-ebill-api/src/service/bill_service/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3192,10 +3192,13 @@ async fn sell_bitcredit_bill_baseline() {
chain.try_add_block(offer_to_sell);
Ok(chain)
});
// Request to sell event should be sent
ctx.transport_service
.expect_send_bill_is_sold_event()
.returning(|_, _| Ok(()));
ctx.transport_service.expect_on_notification_transport(|t| {
t.expect_create_local_bill_notification()
.returning(|_, _, _, _, _| Ok(()));
});
ctx.identity_store
.expect_get_email_confirmations()
.returning(|| Ok(vec![signed_identity_proof_test()]));
Expand Down Expand Up @@ -3269,6 +3272,10 @@ async fn sell_bitcredit_bill_anon_baseline() {
ctx.transport_service
.expect_send_bill_is_sold_event()
.returning(|_, _| Ok(()));
ctx.transport_service.expect_on_notification_transport(|t| {
t.expect_create_local_bill_notification()
.returning(|_, _, _, _, _| Ok(()));
});
ctx.identity_store
.expect_get_email_confirmations()
.returning(|| Ok(vec![signed_identity_proof_test()]));
Expand Down Expand Up @@ -3975,6 +3982,10 @@ async fn check_bill_offer_to_sell_payment_baseline() {
ctx.transport_service
.expect_send_bill_is_sold_event()
.returning(|_, _| Ok(()));
ctx.transport_service.expect_on_notification_transport(|t| {
t.expect_create_local_bill_notification()
.returning(|_, _, _, _, _| Ok(()));
});
ctx.identity_store
.expect_get_email_confirmations()
.returning(|| Ok(vec![signed_identity_proof_test()]));
Expand Down Expand Up @@ -4034,6 +4045,10 @@ async fn check_bills_offer_to_sell_payment_company_is_seller() {
ctx.transport_service
.expect_send_bill_is_sold_event()
.returning(|_, _| Ok(()));
ctx.transport_service.expect_on_notification_transport(|t| {
t.expect_create_local_bill_notification()
.returning(|_, _, _, _, _| Ok(()));
});
ctx.identity_store
.expect_get_email_confirmations()
.returning(|| Ok(vec![signed_identity_proof_test()]));
Expand Down
1 change: 0 additions & 1 deletion crates/bcr-ebill-api/src/service/file_reference_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,6 @@ mod tests {
async fn send_bill_is_accepted_event(&self, event: &bcr_ebill_core::protocol::event::BillChainEvent) -> crate::service::transport_service::Result<()>;
async fn send_request_to_accept_event(&self, event: &bcr_ebill_core::protocol::event::BillChainEvent) -> crate::service::transport_service::Result<()>;
async fn send_request_to_pay_event(&self, event: &bcr_ebill_core::protocol::event::BillChainEvent) -> crate::service::transport_service::Result<()>;
async fn send_bill_is_paid_event(&self, event: &bcr_ebill_core::protocol::event::BillChainEvent) -> crate::service::transport_service::Result<()>;
async fn send_bill_is_endorsed_event(&self, event: &bcr_ebill_core::protocol::event::BillChainEvent) -> crate::service::transport_service::Result<()>;
async fn send_offer_to_sell_event(&self, event: &bcr_ebill_core::protocol::event::BillChainEvent, buyer: &bcr_ebill_core::protocol::blockchain::bill::participant::BillParticipant) -> crate::service::transport_service::Result<()>;
async fn send_bill_is_sold_event(&self, event: &bcr_ebill_core::protocol::event::BillChainEvent, buyer: &bcr_ebill_core::protocol::blockchain::bill::participant::BillParticipant) -> crate::service::transport_service::Result<()>;
Expand Down
31 changes: 28 additions & 3 deletions crates/bcr-ebill-api/src/service/identity_service.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use super::Result;
use crate::constants::{IDENTITY_DOCUMENT_FILE_FIELD, IDENTITY_PROFILE_PICTURE_FILE_FIELD};
use crate::constants::{
IDENTITY_DOCUMENT_FILE_FIELD, IDENTITY_PROFILE_PICTURE_FILE_FIELD,
SAVE_SEED_PHRASE_NOTIFICATION_KEY, SAVE_SEED_PHRASE_NOTIFICATION_REFERENCE_ID,
};
use crate::external::email::EmailClientApi;
use crate::external::file_storage::FileStorageClientApi;
use crate::service::Error;
Expand Down Expand Up @@ -708,6 +711,20 @@ impl IdentityServiceApi for IdentityService {
self.populate_block(&identity, first_block, &keys).await?;
self.on_identity_contact_change(&identity, &keys).await?;

if let Err(e) = self
.block_transport
.notification_transport()
.create_general_notification(
&node_id,
SAVE_SEED_PHRASE_NOTIFICATION_KEY,
Some(SAVE_SEED_PHRASE_NOTIFICATION_REFERENCE_ID.to_string()),
bcr_ebill_core::application::notification::NotificationLevel::ActionRequired,
)
.await
{
error!("Failed to create save seed phrase notification: {e}");
}

// Create and populate identity proof block
if let Some((proof, data)) = email_confirmation {
self.create_identity_proof_block(proof, data, &identity, &keys, &mut identity_chain)
Expand Down Expand Up @@ -1126,6 +1143,11 @@ mod tests {
t.expect_publish_contact().returning(|_, _| Ok(())).once();
t.expect_ensure_nostr_contact().returning(|_| ()).once();
});
transport.expect_on_notification_transport(|t| {
t.expect_create_general_notification()
.returning(|_, _, _, _| Ok(()))
.once();
});

let service = get_service_with_chain_storage(storage, chain_storage, transport);
let res = service
Expand Down Expand Up @@ -1171,8 +1193,11 @@ mod tests {
t.expect_publish_contact().returning(|_, _| Ok(())).once();
t.expect_ensure_nostr_contact().returning(|_| ()).once();
});

// publishes contact info to nostr
transport.expect_on_notification_transport(|t| {
t.expect_create_general_notification()
.returning(|_, _, _, _| Ok(()))
.once();
});

let service = get_service_with_chain_storage(storage, chain_storage, transport);
let res = service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use async_trait::async_trait;
use bcr_common::core::{BillId, NodeId};
use bcr_ebill_core::{
application::ServiceTraitBounds,
application::notification::Notification,
application::notification::{Notification, NotificationLevel},
protocol::Sum,
protocol::blockchain::bill::participant::BillParticipant,
protocol::event::ActionType,
protocol::event::{BillChainEventPayload, Event},
protocol::event::{BillChainEventPayload, BillEventType, Event},
};
use bcr_ebill_persistence::notification::NotificationFilter;
use std::collections::HashMap;
Expand Down Expand Up @@ -43,6 +43,27 @@ pub trait NotificationTransportServiceApi: ServiceTraitBounds {
node_ids: &[NodeId],
) -> Result<HashMap<NodeId, bool>>;

/// Creates a local bill notification for the given node without sending Nostr events.
/// Marks any existing active bill notification as done and pushes to connected clients.
async fn create_local_bill_notification(
&self,
node_id: &NodeId,
bill_id: &BillId,
event_type: BillEventType,
action_type: Option<ActionType>,
sum: Option<Sum>,
) -> Result<()>;

/// Creates a general (non-bill, non-company) notification for the given node.
/// Used for system-level notifications like "save your seed phrase".
async fn create_general_notification(
&self,
node_id: &NodeId,
description: &str,
reference_id: Option<String>,
level: NotificationLevel,
) -> Result<()>;

/// In case a participant did not perform an action (e.g. request to accept, request
/// to pay) in time we notify all bill participants about the timed out action. Will
/// only send the event if the given action can be a timed out action.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ pub trait TransportServiceApi: ServiceTraitBounds {
/// Receiver: Payer, Action: PayBill
async fn send_request_to_pay_event(&self, event: &BillChainEvent) -> Result<()>;

/// Sent when: A bill is paid by: Payer (Bitcoin API)
/// Receiver: Payee, Action: CheckBill
async fn send_bill_is_paid_event(&self, event: &BillChainEvent) -> Result<()>;

/// Sent when: A bill is endorsed by: Previous Holder
/// Receiver: NewHolder, Action: CheckBill
async fn send_bill_is_endorsed_event(&self, event: &BillChainEvent) -> Result<()>;
Expand Down
6 changes: 6 additions & 0 deletions crates/bcr-ebill-api/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,12 @@ pub mod tests {
reference: &str,
notification_type: NotificationType,
) -> Result<Option<Notification>>;
async fn get_latest_by_reference_and_node_id(
&self,
reference: &str,
notification_type: NotificationType,
node_id: &NodeId,
) -> Result<Option<Notification>>;
#[allow(unused)]
async fn list_by_type(&self, notification_type: NotificationType) -> Result<Vec<Notification>>;
async fn mark_as_done(&self, notification_id: &str) -> Result<()>;
Expand Down
Loading
Loading