Zewnętrzne Typy Płatności
Przegląd
Jeśli zaimplementujesz interfejs IExternalPaymentProcessor i zarejestrujesz go poprawnie, w Syrve Instance będzie dostępny nowy system płatności. Dla uproszczenia nazywamy go zewnętrznym typem płatności. Wtyczki używane do zewnętrznych typów płatności muszą być licencjonowane.
Rejestracja
Wtyczka rejestruje zewnętrzny typ płatności za pomocą IOperationService.RegisterPaymentSystem(...). paymentSystem jest wymaganym parametrem, który należy przekazać do tej metody. Jest to instancja klasy, która implementuje IExternalPaymentProcessor. Po rejestracji nowy system płatności będzie dostępny w Syrve Office. Można go znaleźć pod nazwą IExternalPaymentProcessor.PaymentSystemName w polu Typ bezgotówkowy, jeśli wybierzesz Zewnętrzny Typ Płatności jako typ płatności w nowym oknie typu płatności.

Jeśli dodasz typ płatności w ramach tego systemu płatności, ten zewnętrzny typ płatności będzie dostępny na terminalach Syrve POS.
System Płatności definiuje sposób księgowania i zwracania płatności. System płatności jest używany jako pojedyncza instancja w całym Syrve Instance.
Typ Płatności odnosi się do dowolnego systemu płatności. Ma konfigurowalne właściwości, takie jak fiskalność, konta transferowe i inne. Jeśli uczynisz zewnętrzny typ płatności fiskalnym, zostanie on sklasyfikowany jako Typ Kart Bankowych, jeśli pozostawisz go niefiskalnym, będzie on zaliczony do Typu Płatności Bezgotówkowych. W ramach jednego systemu płatności możesz dodać dowolną liczbę typów płatności.
Pozycja Płatności odnosi się do zamówień w Syrve POS. Gdy użytkownik na ekranie płatności lub przedpłaty wybierze dowolną metodę płatności, do zamówienia zostanie dodana pozycja płatności odpowiadająca wybranej metodzie płatności. Pozycje płatności można dodawać za pośrednictwem API (Dodawanie Płatności).
Interfejs IExternalPaymentProcessor
Aby przeprowadzić wymagane procedury biznesowe związane z księgowaniem i zwracaniem płatności dokonanych za pomocą zewnętrznych typów płatności, należy zaimplementować interfejs IExternalPaymentProcessor:
{
string PaymentSystemKey { get; }
string PaymentSystemName { get; }
void CollectData(Guid orderId, Guid paymentTypeId, [NotNull] IUser cashier, IReceiptPrinter printer, UI.IViewManager viewManager, IPaymentDataContext context, UI.IProgressBar progressBar);
void OnPaymentAdded([NotNull] IOrder order, [NotNull] IPaymentItem paymentItem, [NotNull] IUser cashier, [NotNull] IOperationService operationService, IReceiptPrinter printer, UI.IViewManager viewManager, IPaymentDataContext context, UI.IProgressBar progressBar);
bool OnPreliminaryPaymentEditing([NotNull] IOrder order, [NotNull] IPaymentItem paymentItem, [NotNull] IUser cashier, [NotNull] IOperationService operationService, IReceiptPrinter printer, UI.IViewManager viewManager, IPaymentDataContext context, UI.IProgressBar progressBar);
void Pay(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context, IProgressBar progressBar);
void EmergencyCancelPayment(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context, IProgressBar progressBar);
void ReturnPayment(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context, IProgressBar progressBar);
void ReturnPaymentWithoutOrder(decimal sum, Guid paymentTypeId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IProgressBar progressBar);
void PaySilently(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IPaymentDataContext context);
void EmergencyCancelPaymentSilently(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IPaymentDataContext context);
bool CanPaySilently(decimal sum, Guid? orderId, Guid paymentTypeId, IPaymentDataContext context);
}
Gdzie:
-
PaymentSystemKey to unikalny klucz, który nowy zewnętrzny system płatności otrzymuje podczas rejestracji.
-
PaymentSystemName to nazwa wyświetlana w interfejsie użytkownika Syrve Instance.
Metody Przetwarzania Płatności
Gdy użytkownik wybiera typ płatności na ekranie płatności Syrve POS, określa kwotę i naciska przycisk Zapłać lub gdy użytkownik dokonuje przedpłaty za pomocą określonego typu płatności, kontrola użyje metody Pay():
void Pay(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context, IProgressBar progressBar);
Gdzie:
-
sum – kwota płatności
-
orderId – ID zamówienia Syrve POS
-
paymentTypeId – ID typu płatności Lista wszystkich typów płatności może być uzyskana za pomocą metody IOperationService.GetPaymentTypes(); konkretny typ płatności można uzyskać za pomocą metody OperationService_TryGetPaymentTypeById(...)
-
transactionId – ID transakcji
-
pointOfSale – Punkt sprzedaży, w którym przetwarzana jest ta pozycja płatności
-
cashier – kasjer
-
printer – instancja IReceiptPrinter, która umożliwia drukowanie na drukarce paragonów Syrve POS
-
viewManager – instancja IViewManager używana do wyświetlania okien podczas przetwarzania płatności
-
context – instancja IPaymentDataContext używana do zapisywania danych w pozycji płatności
-
progressBar – instancja IProgressBar używana do zmiany tekstu na pasku postępu podczas przetwarzania płatności
Szczegóły dotyczące sygnatury obiektu można znaleźć w dokumentacji.
Jeśli na przykład potrzebujesz integracji z systemem hotelowym:
-
Podczas przyjmowania płatności musisz poprosić użytkowników o podanie numeru pokoju lub okazanie klucza do pokoju.
-
Następnie odwołaj się do usługi hotelowej.
-
Jeśli się powiedzie, wydrukuj paragon z kwotą i nazwiskiem gościa otrzymanym z systemu hotelowego.
Przejdź do Syrve i zapisz wprowadzony numer lub przeciągniętą kartę, aby zobaczyć szczegóły w raportach OLAP.
-
Jeśli wymagany jest zwrot, musisz wiedzieć, czy użyto karty, czy numeru.
-
Jeśli się nie powiedzie, anuluj płatność.
[Serializable]
internal class IsCardClass
{
public bool IsCard;
}
public void Pay(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, IPointOfSale pointOfSale, IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context, IProgressBar progressBar)
{
// Pokaż dialog wprowadzania numeru i przeciągania karty w Syrve POS
var input = viewManager.ShowInputDialog("Wprowadź numer lub przeciągnij kartę", InputDialogTypes.Card | InputDialogTypes.Number);
string room = null;
string cardTrack = null;
// Jeśli wprowadzono numer, wynik to NumberInputDialogResult
var roomNum = input as NumberInputDialogResult;
if (roomNum != null)
room = roomNum.Number.ToString();
// Jeśli przeciągnięto kartę, wynik to CardInputDialogResult
var card = input as CardInputDialogResult;
if (card != null)
cardTrack = card.FullCardTrack;
if (room == null && cardTrack == null)
// Nic nie wprowadzono, operacja jest anulowana.
throw new PaymentActionFailedException("Nie wprowadzono danych.");
// Pobierz zamówienie za pomocą API po ID za pomocą IOperationService.
var order = PluginContext.Operations.TryGetOrderById(orderId.Value);
// Uruchamianie losowych metod. Na przykład, realizacja płatności w jakimś hotelSystem, który zwróci nazwisko gościa, jeśli płatność zostanie zaakceptowana, i null, jeśli płatność zostanie odrzucona.
var guestName = hotelSystem.ProcessPaymentOnGuest(cardTrack, room, order?.Number, transactionId, sum);
if (guestName == null)
// Płatność nie została przetworzona, operacja jest anulowana.
throw new PaymentActionFailedException("Płatność nie została przetworzona.");
// Przygotowanie paragonu do drukowania. Paragon zawiera XElement
var slip = new ReceiptSlip
{
Doc = new XElement(Tags.Doc,
new XElement(Tags.Pair, "Gość", guestName),
new XElement(Tags.Pair, "Kwota", sum))
};
// Drukowanie.
printer.Print(slip);
var cardInfoData = new IsCardClass { IsCard = card != null };
var cardType = cardInfoData.IsCard
? "Karta Systemu Hotelowego"
: "Pokój Systemu Hotelowego";
// Zapisywanie danych do raportów.
context.SetInfoForReports(room ?? cardTrack, cardType);
// Zapisywanie danych do użycia przy zwrocie.
context.SetRollbackData(cardInfoData);
}
Wyjątek PaymentActionFailedException służy do przerwania operacji płatności. Użytkownik Syrve POS zobaczy komunikat o wyjątku. Ma to sens, jeśli wystąpiły jakiekolwiek problemy podczas komunikacji z zewnętrzną usługą, płatność nie może zostać przetworzona, a użytkownik musi zostać poinformowany o przyczynach.
Aby cicho przerwać operację, można użyć wyjątku PaymentActionCancelledException. Ma to sens, jeśli w procesie płatności wyświetlane jest okno dialogowe, a użytkownik wybiera Anuluj.
Argumenty IReceiptPrinter, IViewManager i IPaymentDataContext istnieją tylko podczas działania metody; po zakończeniu metody instancje są usuwane. Nie ma więc sensu zapisywać ich jako zmienne, ponieważ nie można ich używać poza metodą.
Cicha Płatność
Czasami firmy potrzebują rozwiązań do realizacji płatności wtyczkami wewnątrz samych wtyczek, ale bez ekranu kasy Syrve POS. W tym celu wtyczka powinna zaimplementować metodę CanPaySilently procesora płatności wtyczki. Wynik metody to odpowiedź na pytanie: „Czy wtyczka może przetwarzać ciche płatności?” Aby to było możliwe, do zamówienia należy dodać pozycję płatności wtyczki. Ciche płatności mogą wymagać wywołania metody ProcessPrepay z ustawioną flagą isProcessed na false. SDK pokazuje przypadek użycia klasy napisanej przez użytkownika z właściwością SilentPay:
[Serializable]
public class PaymentAdditionalData
{
public bool SilentPay { get; set; }
}
private string Serialize<T>(T data) where T : class
{
using (var sw = new StringWriter())
using (var writer = XmlWriter.Create(sw))
{
new XmlSerializer(typeof(T)).Serialize(writer, data);
return sw.ToString();
}
}
private void AddAndProcessExternalPrepay()
{
var order = PluginContext.Operations.GetOrders().Last(o => o.Status == OrderStatus.New);
var paymentType = PluginContext.Operations.GetPaymentTypes().Single(i => i.Kind == PaymentTypeKind.External && i.Name == "SamplePaymentType");
var additionalData = new ExternalPaymentItemAdditionalData
{
CustomData = Serialize(new PaymentAdditionalData {SilentPay = true})
};
var credentials = PluginContext.Operations.AuthenticateByPin("777");
var paymentItem = PluginContext.Operations.AddExternalPaymentItem(order.ResultSum, false, additionalData, paymentType, order, credentials);
PluginContext.Operations.ProcessPrepay(credentials, order, paymentItem);
}
Z kolei Syrve POS wysyła określoną zserializowaną klasę do kontekstu płatności (IPaymentContext), a następnie ta klasa jest wyodrębniana i deserializowana w metodzie CanPaySilently.
W zależności od odpowiedzi CanPaySilently, Syrve POS wywoła metodę procesora płatności Pay lub PaySilently. W ten sposób wtyczka definiuje sposób, w jaki nowa płatność powinna być przetwarzana.
Metody Zwrotu
void EmergencyCancelPayment(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context, IProgressBar progressBar);
void ReturnPayment(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context, IProgressBar progressBar);
void ReturnPaymentWithoutOrder(decimal sum, Guid paymentTypeId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IProgressBar progressBar);
void EmergencyCancelPaymentSilently(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IPaymentDataContext context);
Metody EmergencyCancelPayment() i ReturnPayment() są wywoływane, gdy użytkownik dokonuje zwrotu na Syrve POS.
Metoda ReturnPayment() przejmuje kontrolę, gdy na ekranie zamkniętego zamówienia naciśnięty zostanie przycisk Refund lub Delete. Lub jeśli użytkownik usunie zaksięgowaną płatność. EmergencyCancelPayment() przejmuje kontrolę, gdy zaksięgowana płatność jest anulowana w otwartym zamówieniu. Na przykład, jeśli płatność fiskalna jest anulowana z powodu błędu drukowania paragonu fiskalnego. Jeśli w tym ostatnim przypadku nie jest wymagana żadna specyficzna logika, metoda ReturnPayment() może być wywołana z metody EmergencyCancelPayment().
Metody przyjmują te same parametry, co metody płatności. transactionId jest taki sam jak ten wysłany do operacji wykonanej wcześniej - Pay().
Metody są uznawane za zakończone, jeśli w procesie nie wystąpiły żadne wyjątki, takie jak PaymentActionFailedException lub PaymentActionCancelledException. Jeśli takie wyjątki miały miejsce, operacja zwrotu jest przerwana, podobnie jak w przypadku płatności.
Przykład kodu integracji hotelowej. Metoda zwrotu anuluje transakcję i drukuje paragon z kwotą zwrotu i zapisanymi szczegółami: karta przeciągnięta lub numer wprowadzony.
[Serializable]
public class IsCardClass
{
public bool IsCard;
}
public void ReturnPayment(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context, IProgressBar progressBar)
{
// Uruchamianie losowych metod. Na przykład, płatność jest zwracana przez ID transakcji w jakimś systemie hotelowym, który zwróci true, jeśli płatność zostanie zwrócona pomyślnie, i false, jeśli zwrot się nie powiedzie.
var success = hotelSystem.ProcessReturnPayment(transactionId);
if (!success)
throw new PaymentActionFailedException("Failed to refund.");
// Pobieranie danych zapisanych w pozycji płatności.
var isCard = context.GetRollbackData<IsCardClass>();
var slip = new ReceiptSlip
{
Doc = new XElement(Tags.Doc,
new XElement(Tags.Pair, "Zwrot płatności", sum),
new XElement(Tags.Pair, "Czy użyto karty", isCard.IsCard ? "TAK" : "NIE" ))
};
printer.Print(slip);
}
public void EmergencyCancelPayment(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context, IProgressBar progressBar)
{
ReturnPayment(sum, orderId, paymentTypeId, transactionId, pointOfSale, cashier, printer, viewManager, context, progressBar);
Metoda ReturnPaymentWithoutOrder() jest wywoływana, gdy ma miejsce zwrot zewnętrznej płatności. Począwszy od wersji Syrve 6.2.2, możesz zwracać pozycje za pomocą zewnętrznych typów, nawet jeśli zamówienia nie są przedpłacone. Aby móc wybrać zewnętrzny typ w interfejsie zwrotu, musisz zarejestrować system płatności z opcjonalnym parametrem canProcessPaymentReturnWithoutOrder = true. To jest
var disposable = PluginContext.Operations.RegisterPaymentSystem(paymentSystem, true).
W przeciwieństwie do innych metod wymienionych powyżej, metoda ReturnPaymentWithoutOrder() nie ma ani kontekstu zamówienia, ani przedpłaty. Zakładamy, że takie szczegóły jak kwota i metoda płatności są wystarczające do dokonania zwrotu. W procesie możesz pokazywać użytkownikom dialogi i drukować paragony, tak jak w przypadku innych metod wymienionych powyżej.
Metody Zbierania Danych
void CollectData(Guid orderId, Guid paymentTypeId, [NotNull] IUser cashier, IReceiptPrinter printer, UI.IViewManager viewManager, IPaymentDataContext context, UI.IProgressBar progressBar);
void OnPaymentAdded([NotNull] IOrder order, [NotNull] IPaymentItem paymentItem, [NotNull] IUser cashier, [NotNull] IOperationService operationService, IReceiptPrinter printer, UI.IViewManager viewManager, IPaymentDataContext context, UI.IProgressBar progressBar);
bool OnPreliminaryPaymentEditing([NotNull] IOrder order, [NotNull] IPaymentItem paymentItem, [NotNull] IUser cashier, [NotNull] IOperationService operationService, IReceiptPrinter printer, UI.IViewManager viewManager, IPaymentDataContext context, UI.IProgressBar progressBar);
Jeśli potrzebujesz zebrać jakiekolwiek dane w momencie dodania zewnętrznej pozycji płatności do zamówienia, a nie w momencie naciśnięcia przycisku Pay, możesz odpowiednio zmienić metodę CollectData().
Metoda OnPaymentAdded() jest wywoływana, gdy pozycja płatności zostanie dodana do zamówienia. Ta metoda jest wyjątkowa, ponieważ ma IOperationService operationService jako jeden z jej argumentów. W przeciwieństwie do PluginContext.Operations, ta instancja ma uprawnienia do zmiany bieżącego zamówienia. Może to być wymagane, na przykład, aby ustawić kwotę pozycji płatności lub nawet dodać jakąkolwiek pozycję menu do zamówienia. Funkcja OnPreliminaryPaymentEditing() jest wywoływana podczas edytowania przedpłat. Ta metoda może również zmienić bieżące zamówienie za pomocą argumentu IOperationService operationService. Metoda zwraca bool, którego znaczenie jest następujące: czy kwota pozycji przedpłaty może być zmieniona w interfejsie użytkownika po zakończeniu metody.
Zamykanie i otwieranie zmian kasowych w Syrve POS
Niektóre zewnętrzne systemy płatności muszą wykonać określone działania po swojej stronie w momencie otwierania lub zamykania zmiany kasowej w Syrve POS. Na przykład systemy bankowe muszą przeprowadzić weryfikację przy zamykaniu zmiany kasowej. W tym celu należy subskrybować INotificationService.SubscribeOnCafeSessionOpening i INotificationService.SubscribeOnCafeSessionClosing.
Kiedy otwierasz lub zamykasz zmianę kasową, odpowiedni obserwator otrzymuje nowe zdarzenie. Przykład kodu, który w momencie otwierania lub zamykania zmiany, drukuje klucz systemu płatności i informację, czy zmiana jest zamknięta czy otwarta:
ctor
{
// ...
PluginContext.Notifications.SubscribeOnCafeSessionClosing(CafeSessionClosing);
PluginContext.Notifications.SubscribeOnCafeSessionOpening(CafeSessionOpening)
}
private void CafeSessionOpening([NotNull] IReceiptPrinter printer, [NotNull] IProgressBar progressBar)
{
PluginContext.Log.Info("Otwarcie sesji kawiarnianej.");
var message =
"Nie mogę połączyć się z moim serwerem i otworzyć zmiany.";
PluginContext.Operations.AddNotificationMessage(message, "SamplePaymentPlugin");
}
private void CafeSessionClosing([NotNull] IReceiptPrinter printer, [NotNull] IProgressBar progressBar)
{
PluginContext.Log.Info("Zamykanie sesji kawiarnianej.");
var slip = new ReceiptSlip
{
Doc = new XElement(Tags.Doc,
new XElement(Tags.Center, PaymentSystemKey),
new XElement(Tags.Center, "Sesja kawiarniana zamknięta."))
};
printer.Print(slip);
}
Jeśli potrzebujesz ostrzec użytkownika, możesz użyć powiadomień. Wyjątki, które występują podczas działania CafeSessionOpening() i CafeSessionClosing() nie przerywają operacji otwierania lub zamykania zmiany kasowej w Syrve POS. Co więcej, jeśli procesor napotka jakieś wyjątki, jest uznawany za uszkodzony i nie jest wywoływany, dopóki wtyczka nie zostanie ponownie uruchomiona.