iiko / front.api.sdk

iikoFront Api SDK (part of iikoRMS)
34 stars 22 forks source link

Плагин для iiko 6.4 API V6 на iiko 6.3 не работает #80

Closed Azamatjon closed 5 years ago

Azamatjon commented 5 years ago

Здравствуйте, Написал плагин под SamplePaymentPlugin. И он работает на версии iiko 6.4 API V6. Поставил на продакшн на iiko 6.3. Перестал работать. Посмотрел в логах, там написано якобы не существует метод CollectData. В чём проблема? Хотя все методы прописаны.

Код плагина:

internal sealed class ExternalPaymentProcessorSample : MarshalByRefObject, IExternalPaymentProcessor, IDisposable
{
    public override object InitializeLifetimeService() { return null; }

    private const string paymentSystemKey = "opayment";
    private const string paymentSystemName = "O!Dengi";

    public string PaymentSystemKey => paymentSystemKey;
    public string PaymentSystemName => paymentSystemName;

    private AppData appData = null;

    public Dictionary<string, JsonExtender> orderHistory;
    private readonly CompositeDisposable subscriptions;

    public ExternalPaymentProcessorSample()
    {
        this.appData = new AppData();

        subscriptions = new CompositeDisposable
        {
            PluginContext.Notifications.SubscribeOnCafeSessionClosing((p, v) => CafeSessionClosing(p)),
            PluginContext.Notifications.SubscribeOnCafeSessionOpening((p, v) => CafeSessionOpening(p)),

            // Catch bill printing
            PluginContext.Notifications.SubscribeOnBillChequePrinting(AddBillChequeExtensions),
            PluginContext.Notifications.SubscribeOnCashChequePrinting(AddCashChequeExtensions),

        };

        orderHistory = new Dictionary<string, JsonExtender>();

        var threadProcess = new Thread(ServiseStarter);
        threadProcess.SetApartmentState(ApartmentState.STA);
        threadProcess.Start();
    }

    private void ServiseStarter()
    {
        PluginContext.Log.Info("Server-Bills Service started");

        var paymentType = PluginContext.Operations.GetPaymentTypes().LastOrDefault(x => x.Kind == PaymentTypeKind.External && x.Name == this.appData.AppName);

        try
        {
            var orders = PluginContext.Operations.GetOrders().Where(x => (x.Status == OrderStatus.New || x.Status == OrderStatus.Bill) && (x.Payments.Count > 0 && x.Payments.FirstOrDefault(y => y.Type.Equals(paymentType)) != null));

            PluginContext.Log.InfoFormat("FOunde # " + orders.Count());

            foreach (IOrder tmpOrder in orders)
            {
                IPaymentItem orderPaymentItem = tmpOrder.Payments.LastOrDefault(x => x.Type.Equals(paymentType));
                ExternalPaymentItemAdditionalData orderPaymentType = (ExternalPaymentItemAdditionalData)orderPaymentItem.AdditionalData;
                PaymentAdditionalData paymentAdditionalData = Serializer.Deserialize<PaymentAdditionalData>(orderPaymentType.CustomData);

                JsonExtender responseData = new JsonExtender
                {
                    data = new JsonExtenderData
                    {
                        invoice_id = paymentAdditionalData.InvoiceId,
                        IsPaid = false,
                        order_id = tmpOrder.Id.ToString(),
                        qr = paymentAdditionalData.QRLink,
                        status = "processing"
                    }
                };

                orderHistory.Add(tmpOrder.Id.ToString(), responseData);

            }
        } catch (Exception e)
        {
            PluginContext.Log.Error("Order # " + e.Message);
        }

        Timer timer = new Timer(checkPaidBillsByServer, "update", TimeSpan.FromSeconds(this.appData.ServerUpdateTime), TimeSpan.FromSeconds(this.appData.ServerUpdateTime));

        // Service will be working infinity (in ms)
        Thread.Sleep(Timeout.Infinite);
    }

    private void checkPaidBillsByServer(object state)
    {
        PluginContext.Log.InfoFormat("=============================================================================");
        PluginContext.Log.Info("Checking server");
        PluginContext.Log.InfoFormat("=============================================================================");

        foreach (KeyValuePair<string, JsonExtender> order in orderHistory)
        {
            if (order.Value.data.IsPaid == true)
            {
                continue;
            }
            try
            {
                PluginContext.Log.Info("Checking Order: " + order.Key);

                ProviderOperation providerOperation = new ProviderOperation(this.appData);

                JsonExtender responseData = null;
                try
                {
                    var invoiceStatusData = providerOperation.invoiceStatus(order.Key, order.Value.data.invoice_id);
                    responseData = providerOperation.Send(invoiceStatusData, "statusPayment");
                }
                catch (WebException)
                {
                    PluginContext.Log.Error("Нет соединение с сервером. Убедитесь, что у вас имеется интернет соединение...");
                }

                if (responseData.data.status == "processing")
                {
                    continue;
                }
                else if (responseData.data.payments.Count > 0)
                {
                    JsonExtenderPaymentsData transaction = responseData.data.payments[0];
                    switch (transaction.status)
                    {
                        case "approved":
                            order.Value.data.IsPaid = true;
                            order.Value.data.trans_id = transaction.trans_id;

                            var orderObj = GetOrderSafe(Guid.Parse(order.Key));

                            PluginContext.Operations.AddNotificationMessage("Оплата заказа №" + orderObj.Number + " от " + orderObj.Waiter.Name + " в сумме: " + (Int32.Parse(transaction.amount) / 100) + "сом успешно выполнена. Пожалуйста завершите заказ нажимая кнопку Оплатить.", "Успех");

                            PluginContext.Log.Info("Approved");
                            break;
                        case "cancelled":
                            order.Value.data.IsPaid = false;
                            order.Value.data.trans_id = transaction.trans_id;

                            PluginContext.Log.Info("Cancelled");
                            break;
                        default:

                            PluginContext.Log.Error("Couldn't define invoice status");
                            break;
                    }
                } else
                {
                    PluginContext.Log.Error("Couldn't define invoice status");
                }
            } catch (Exception e)
            {
                PluginContext.Log.Error("Checking server error: " + e.Message);
            }
        }
    }

    private CashCheque AddCashChequeExtensions(Guid orderId)
    {
        var transactionData = orderHistory[orderId.ToString()];

        return new CashCheque
        {
            AfterCheque = new XElement(
                Tags.Justify,
                    new XElement(Tags.NewParagraph),
                    new XElement(Tags.NewParagraph),
                    new XElement(Tags.NewParagraph),
                    new XElement(Tags.Left, "ID транзакции: " + transactionData.data.payments[0].trans_id),
                    new XElement(Tags.Left, "Время оплаты: " + transactionData.data.payments[0].date_pay.Split('.')[0]),
                    new XElement(Tags.Left, "ФИО плательшика: " + transactionData.data.payments[0].fname + " " + transactionData.data.payments[0].lname)
                )
        };
    }

    // Catching BillPrinting
    [NotNull]
    private BillCheque AddBillChequeExtensions(Guid orderId)
    {
        return AddUniqueChequeExtensions(orderId);
    }

    [NotNull]
    private BillCheque AddUniqueChequeExtensions(Guid orderId)
    {
        var order = GetOrderSafe(orderId);

        var billCheque = new BillCheque();

        if (order.Payments.Select(x => x.Type.Name.Equals(paymentSystemName)).Count() > 0)
        {
            JsonExtender responseData = null;

            if (!orderHistory.ContainsKey(orderId.ToString()))
            {               
                ProviderOperation providerOperation = new ProviderOperation(this.appData);

                try
                {
                    var invoiceData = providerOperation.createInvoice(order, "Paying with O!Dengi");
                    responseData = providerOperation.Send(invoiceData, "createInvoice");
                }
                catch (WebException)
                {
                    return new BillCheque
                    {
                        AfterFooter = new XElement(
                            Tags.Justify,
                                new XElement(Tags.NewParagraph),
                                new XElement(Tags.Center, "Нет соединение с сервером. Убедитесь, что у вас имеется интернет соединение...")
                            )
                    };
                }

                // Custom cache list
                orderHistory.Add(orderId.ToString(), responseData);
            }
            else
            {
                responseData = orderHistory[orderId.ToString()];
            }

            billCheque.AfterFooter = new XElement(
                Tags.Justify,
                    new XElement(Tags.QRCode, this.appData.InvoiceLink + responseData.data.invoice_id),
                    new XElement(Tags.Center, this.appData.AfterQRMessage)
                );

            billCheque.AfterHeader = new XElement(Tags.Table,
                     new XElement(Tags.Columns,
                         new XElement(Tags.Column, 
                            new XAttribute(Attributes.AutoWidth, AttributeValues.Empty)
                            ),
                         new XElement(Tags.Column,
                            new XAttribute(Attributes.AutoWidth, AttributeValues.Empty)
                            )
                         ),
                     new XElement(Tags.Cells,
                         new XElement(Tags.TextCell, "Инвойс №" + responseData.data.invoice_id)
                         )
                     );
        }

        return billCheque;
    }

    public void CollectData(Guid orderId, Guid paymentTypeId, [NotNull] IUser cashier, IReceiptPrinter printer,
        IViewManager viewManager, IPaymentDataContext context)
    {
        PluginContext.Log.InfoFormat("Collect data for order ({0})", orderId);

        viewManager.ChangeProgressBarMessage("Ожидайте. Идёт подключение с сервером...");
        try
        {
            IOrder order = this.GetOrderSafe(orderId);

            JsonExtender responseData = null;
            if (!orderHistory.ContainsKey(orderId.ToString()))
            {
                ProviderOperation providerOperation = new ProviderOperation(this.appData);

                string paymentDescription = order.Items.ToString();

                try
                {
                    var invoiceData = providerOperation.createInvoice(order, ((paymentDescription.Length >= 1000) ? paymentDescription.Substring(0, 999) : paymentDescription));
                    responseData = providerOperation.Send(invoiceData, "createInvoice");
                } catch (WebException)
                {
                    throw new PaymentActionFailedException("Нет соединение с сервером. Убедитесь, что у вас имеется интернет соединение...");
                }

                orderHistory.Add(orderId.ToString(), responseData);
            } else
            {
                responseData = orderHistory[orderId.ToString()];
            }

            var rollbackData = new CollectedData { InvoiceId = responseData.data.invoice_id, OrderId = responseData.data.order_id, QRLink = responseData.data.qr };
            context.SetRollbackData(rollbackData);

            PluginContext.Log.Info("Invoice_id: " + responseData.data.invoice_id);
            PluginContext.Log.Info("qr: " + responseData.data.qr);

        } catch (Exception e)
        {
            throw new PaymentActionFailedException("Error: " + e.Message);
        }

    }

    public void OnPaymentAdded(IOrder order, IPaymentItem paymentItem, [NotNull] IUser cashier, [NotNull] IOperationService operations, IReceiptPrinter printer, IViewManager viewManager,
        IPaymentDataContext context)
    {
        PluginContext.Log.InfoFormat("=============================================================================");
        PluginContext.Log.InfoFormat("OnPaymentAdded");
        PluginContext.Log.InfoFormat("=============================================================================");

        switch (order.Status)
        {
            case OrderStatus.New:
            case OrderStatus.Bill:

                PluginContext.Log.InfoFormat("OnPaymentAdded before");
                var dataFromContext = context.GetRollbackData<CollectedData>();
                PluginContext.Log.InfoFormat("OnPaymentAdded after");

                try
                {
                    var editSession = operations.CreateEditSession();

                    editSession.DeletePaymentItem(paymentItem, order);

                    var additionalData = new ExternalPaymentItemAdditionalData { CustomData = Serializer.Serialize(new PaymentAdditionalData { SilentPay = true, InvoiceId = dataFromContext.InvoiceId, OrderId = order.Id.ToString(), CashierId = cashier.Id.ToString(), QRLink = dataFromContext.QRLink }) };

                    var paymentType = PluginContext.Operations.GetPaymentTypes().Last(x => x.Kind == PaymentTypeKind.External && x.Name == this.appData.AppName);

                    var paymentTypes = PluginContext.Operations.GetPaymentTypes();

                    editSession.AddExternalPaymentItem(order.ResultSum, false, additionalData, paymentType, order);
                    operations.SubmitChanges(operations.GetCredentials(appData.PaymentProcessorUserPin), editSession);

                    if (!orderHistory.ContainsKey(order.Id.ToString()))
                    {
                        viewManager.ShowOkPopup("Печать инвойса", "Печатать чек инвойса можно переходя на вкладку ЗАКАЗ -> нажимая на ПРЕЧЕК");
                    }

                } catch (Exception e)
                {
                    throw new PaymentActionFailedException("Error: " + e.Message);
                }

                break;
            default:
                throw new ArgumentOutOfRangeException("OnPaymentAdded Order with Status: " + order.Status + " cannot be paid.");
        }

    }

    // Implementation of preliminary payment editing method. Executes when plug-in preliminary payment item is going to be edited on preliminary payments page,
    // immediately after user presses the edit button.
    public bool OnPreliminaryPaymentEditing(IOrder order, IPaymentItem paymentItem, IUser cashier, IOperationService operationService,
        IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context)
    {
        PluginContext.Log.InfoFormat("OnPreliminaryPaymentEditing");
        //enabled standard numpad editing.
        return true;
    }

    public void ReturnPaymentWithoutOrder(decimal sum, Guid paymentTypeId, IPointOfSale pointOfSale,
        IUser cashier, IReceiptPrinter printer, IViewManager viewManager)
    {
        PluginContext.Log.InfoFormat("ReturnPaymentWithoutOrder");
        // if you need to implement return payment without iiko order, you should register your IExternalPaymentProcessor as 'canProcessPaymentReturnWithoutOrder = true'
        throw new PaymentActionFailedException("Not supported action");
    }

    // Implementation of payment method. Executes when order contains plug-in payment item type and order payment process begins.
    public void Pay(decimal sum, [NotNull] IOrder order, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier,
        [NotNull] IOperationService operations, IReceiptPrinter printer, IViewManager viewManager, IPaymentDataContext context)
    {

    }

    // Implementation of emergency cancel payment method. Executes when already processed plug-in payment item is removing from order.
    // May be the same method with ReturnPayment() if there is no difference in payment system guidelines.
    public void EmergencyCancelPayment(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer,
        IViewManager viewManager, IPaymentDataContext context)
    {
        PluginContext.Log.InfoFormat("EmergencyCancelPayment");
        PluginContext.Log.InfoFormat("Cancel {0}", sum);
        ReturnPayment(sum, orderId, paymentTypeId, transactionId, pointOfSale, cashier, printer, viewManager, context);
    }

    public void PaySilently(decimal sum, Guid orderId, Guid paymentTypeId, Guid transactionId, IPointOfSale pointOfSale, IUser cashier, IReceiptPrinter printer, IPaymentDataContext context)
    {
        PluginContext.Log.InfoFormat("PaySilently");

        PluginContext.Log.InfoFormat("=============================================================================");
        PluginContext.Log.InfoFormat("PaySilently");
        PluginContext.Log.InfoFormat("=============================================================================");

        var dataToAnalyse = context.GetCustomData<PaymentAdditionalData>();

        PluginContext.Log.InfoFormat("Pay InvoiceId: " + dataToAnalyse.InvoiceId);
        PluginContext.Log.InfoFormat("Pay OrderId: " + dataToAnalyse.OrderId);

        IOrder order = GetOrderSafe(orderId);
        PluginContext.Log.InfoFormat("1");
        JsonExtender responseData = null;

        PluginContext.Log.InfoFormat("2");
        ProviderOperation providerOperation = new ProviderOperation(this.appData);
        PluginContext.Log.InfoFormat("3");
        try
        {
            PluginContext.Log.InfoFormat("4");
            var invoiceStatusData = providerOperation.invoiceStatus(dataToAnalyse.OrderId, dataToAnalyse.InvoiceId);
            PluginContext.Log.InfoFormat("5");
            responseData = providerOperation.Send(invoiceStatusData, "statusPayment");
            PluginContext.Log.InfoFormat("6");
        }
        catch (WebException)
        {
            throw new PaymentActionFailedException("Нет соединение с сервером. Убедитесь, что у вас имеется интернет соединение...");
        }

        PluginContext.Log.InfoFormat("7");
        // Custom cache list
        if (!orderHistory.ContainsKey(orderId.ToString()))
        {
            PluginContext.Log.InfoFormat("8");
            orderHistory.Add(orderId.ToString(), responseData);
        }
        else
        {
            PluginContext.Log.InfoFormat("9");
            orderHistory[orderId.ToString()] = responseData;
        }

        var mess = "Пожалуйста заново выберите оплату " + this.PaymentSystemName;

        if (responseData.data.status == "processing")
        {
            throw new PaymentActionFailedException("Не была оплачена. " + mess);
        }
        else if (responseData.data.payments.Count > 0)
        {

            JsonExtenderPaymentsData transaction = responseData.data.payments[0];

            switch (transaction.status)
            {
                case "approved":

                    Decimal transactionSum = Decimal.Parse(transaction.amount);
                    Decimal orderSum = order.FullSum * 100;

                    if (transactionSum != orderSum)
                    {

                        MessageBox.Show("Внимание: сумма оплаты заказа была изменена с момента создания инвойса. Будет произведен возврат денег (отмена транзакции). После успешной отмены транзакции заново выберите " + appData.AppName + " и попросите оплатить счёт заново.", "Возврат оплаты", MessageBoxButton.OK);

                        try
                        {

                            var invoiceStatusData = providerOperation.refund(transaction.trans_id);
                            var refoundData = providerOperation.Send(invoiceStatusData, "voidPayment");

                            if (refoundData.data.success == true)
                            {
                                ReceiptSlip receipt = this.refundChequeFormatter(responseData, cashier);
                                printer.Print(receipt);
                            }
                            else
                            {
                                throw new PaymentActionFailedException("Возврат денег через " + appData.AppName + " не может быть выполнена.");
                            }

                        } catch (WebException)
                        {
                            throw new PaymentActionFailedException("Возврат денег через " + appData.AppName + " не может быть выполнена. Нет соединение с сервером. Убедитесь, что у вас имеется интернет соединение...");
                        }
                    }

                    if (orderHistory.ContainsKey(order.Id.ToString()))
                    {
                        JsonExtender tmp = orderHistory[order.Id.ToString()];
                        tmp.data.IsPaid = true;
                        tmp.data.trans_id = transaction.trans_id;
                    }

                    PluginContext.Log.Info("Оплата проиошла успешно");
                    break;
                case "cancelled":
                    if (orderHistory.ContainsKey(order.Id.ToString()))
                    {
                        JsonExtender orderObj_ = orderHistory[order.Id.ToString()];
                        orderObj_.data.IsPaid = false;
                        orderObj_.data.trans_id = transaction.trans_id;
                    }

                    PluginContext.Log.Info("Оплата отменена");
                    throw new PaymentActionFailedException("Оплата была отменена. " + mess);

                default:
                    throw new PaymentActionFailedException("Не была оплачена. " + mess);
            }
        }
        else
        {
            throw new PaymentActionFailedException("Что то пошло не так. " + mess);
        }

        var data = context.GetCustomData();
        // You can get order from api by id via operationService.
        context.SetInfoForReports(data, "data");
    }

    private ReceiptSlip refundChequeFormatter(JsonExtender paymentData, IUser cashier)
    {
        JsonExtenderPaymentsData transactionData = paymentData.data.payments[0];

        IOrder order = this.GetOrderSafe(Guid.Parse(transactionData.order_id));

        var slip = new ReceiptSlip
        {
            Doc =
                new XElement(Tags.Doc,
                    new XElement(Tags.Justify,
                        new XElement(Tags.Center, "Возврат денег (Отмена транзакции)"),
                        new XElement(Tags.Center, "*"),
                        new XElement(Tags.Center, "-------"),
                        new XElement(Tags.Center, "*"),
                        new XElement(Tags.Right, "Тип оплаты: " + appData.AppName),
                        new XElement(Tags.Left, "Номер заказа: " + order.Number),
                        new XElement(Tags.Left, "Инвойс № " + transactionData.trans_id),
                        new XElement(Tags.Left, "Кассир: " + cashier.Name),
                        new XElement(Tags.Center, "*"),
                        new XElement(Tags.Center, "----  Оплатил  ---"),
                        new XElement(Tags.Center, "*"),
                        new XElement(Tags.Left, "ФИО: " + transactionData.fname + " " + transactionData.lname),
                        new XElement(Tags.Left, "Номер телефона: " + transactionData.mobile),
                        new XElement(Tags.Center, "*"),
                        new XElement(Tags.Center, "---  Время  ----"),
                        new XElement(Tags.Center, "*"),
                        new XElement(Tags.Left, "Дата оплаты: " + transactionData.date_pay.Split('.')[0]),
                        new XElement(Tags.Left, "Дата возврата: " + DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss"))
                        )
                    )
        };

        return slip;
    }

    public void EmergencyCancelPaymentSilently(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, IPointOfSale pointOfSale, IUser cashier, IReceiptPrinter printer, IPaymentDataContext context)
    {
        PluginContext.Log.InfoFormat("EmergencyCancelPaymentSilently");
        PluginContext.Log.InfoFormat("Silent cancel {0}", sum);
        //there are no instances of viewManager and progressBar
        ReturnPayment(sum, orderId, paymentTypeId, transactionId, pointOfSale, cashier, printer, null, context);
    }

    // Implementation of return payment method. Executes when order contains processed plug-in payment item and order is storning.
    public void ReturnPayment(decimal sum, Guid? orderId, Guid paymentTypeId, Guid transactionId, [NotNull] IPointOfSale pointOfSale, [NotNull] IUser cashier, IReceiptPrinter printer,
        IViewManager viewManager, IPaymentDataContext context)
    {
        PluginContext.Log.InfoFormat("ReturnPayment");
        PluginContext.Log.InfoFormat("Order id  {0}", orderId);

        var data = context.GetCustomData<PaymentAdditionalData>();

        ProviderOperation providerOperation = new ProviderOperation(this.appData);

        JsonExtender responseData = null;
        try
        {
            var invoiceStatusData = providerOperation.invoiceStatus(data.OrderId, data.InvoiceId);
            responseData = providerOperation.Send(invoiceStatusData, "statusPayment");

            printer.Print(this.refundChequeFormatter(responseData, cashier));
            PluginContext.Log.InfoFormat("statusPayment");
        }
        catch (WebException)
        {
            throw new PaymentActionFailedException("Нет соединение с сервером. Убедитесь, что у вас имеется интернет соединение...");
        }

        if (responseData.data.status == "processed")
        {
            PluginContext.Log.InfoFormat("processed");
            try
            {

                ProviderOperation refundOperation = new ProviderOperation(this.appData);
                var refundData = providerOperation.refund(responseData.data.trans_id);
                var responseData2 = providerOperation.Send(refundData, "voidPayment");
                PluginContext.Log.InfoFormat("responseData2");
                if (responseData2.data.success == true)
                {
                    ReceiptSlip receipt = this.refundChequeFormatter(responseData, cashier);
                    printer.Print(receipt);
                } else
                {
                    throw new PaymentActionFailedException("Возврат денег через " + appData.AppName + " не может быть выполнена.");
                }

            }
            catch (WebException)
            {
                throw new PaymentActionFailedException("Нет соединение с сервером. Убедитесь, что у вас имеется интернет соединение...");
            }

            catch (Exception e)
            {
                throw new PaymentActionFailedException("Error: " + e.Message);
            }

        }
    }

    public bool CanPaySilently(decimal sum, Guid? orderId, Guid paymentTypeId, IPaymentDataContext context)
    {
        PluginContext.Log.InfoFormat("CanPaySilently");
        PluginContext.Log.InfoFormat("Checking of payment silent processing {0}, оrder id  {1}", sum, orderId);

        var customData = context.GetCustomData<PaymentAdditionalData>();

        return true;
    }

    [CanBeNull]
    private IOrder GetOrderSafe(Guid? orderId)
    {
        PluginContext.Log.InfoFormat("GetOrderSafe");
        return orderId.HasValue ? PluginContext.Operations.TryGetOrderById(orderId.Value) : null;
    }

    private void CafeSessionClosing([NotNull] IReceiptPrinter printer)
    {
        PluginContext.Log.InfoFormat("CafeSessionClosing");
        PluginContext.Log.Info("Cafe Session Closing.");
        var slip = new ReceiptSlip
        {
            Doc = new XElement(Tags.Doc,
                new XElement(Tags.Center, PaymentSystemKey),
                new XElement(Tags.Center, "Cafe session closed."))
        };
        printer.Print(slip);
    }

    private void CafeSessionOpening([NotNull] IReceiptPrinter printer)
    {
        PluginContext.Log.InfoFormat("CafeSessionOpening");
        PluginContext.Log.Info("Cafe Session Opening.");
        const string message = "SamplePaymentPlugin: 'I can not connect to my server to open operation session. But I'll not stop openning iikoFront cafe session.'";
        PluginContext.Operations.AddNotificationMessage(message, "SamplePaymentPlugin");
    }

    public void Dispose()
    {
        PluginContext.Log.InfoFormat("Dispose");
        subscriptions?.Dispose();
    }
}

Прилагаю лог плагина: [2019-08-02 11:16:39,938] INFO [ 1] - ================================================================================ [2019-08-02 11:16:39,944] INFO [ 1] - Resto.Front.Api.Host ver. 6.3.3014.0 [2019-08-02 11:16:39,944] INFO [ 1] - ================================================================================ [2019-08-02 11:16:39,944] INFO [ 1] - Host process PaymentSystem.iikoNet (9108) for “Resto.Front.Api.SamplePaymentPlugin.SamplePaymentPlugin” class from “C:\Program Files\iiko\iikoRMS\Front.Net\Plugins\PaymentSystem.iikoNet\Resto.Front.Api.SamplePaymentPlugin.dll” assembly is running in background mode. [2019-08-02 11:16:39,945] INFO [ 1] - Plugin assembly info: iiko Resto.Front.Api.SamplePaymentPlugin v0.0.0.0, (Resto.Front.Api.SamplePaymentPlugin.dll v0.0.0.0, Resto.Front.Api.SamplePaymentPlugin) [2019-08-02 11:16:39,955] INFO [ 1] - Created plugin domain “PaymentSystem.iikoNet” using “C:\Program Files\iiko\iikoRMS\Front.Net\Plugins\PaymentSystem.iikoNet\Resto.Front.Api.SamplePaymentPlugin.dll.config” [2019-08-02 11:16:39,959] INFO [ 1] - Registering ipc channel... [2019-08-02 11:16:40,226] INFO [ 1] - Plugin context initialized, creating plugin instance... [2019-08-02 11:16:40,237] ERROR [ 1] - Couldn't create plugin instance, constructor thrown an exception: System.Reflection.TargetInvocationException: Адресат вызова создал исключение. ---> System.TypeLoadException: Отсутствует реализация метода "CollectData" в типе "Resto.Front.Api.SamplePaymentPlugin.ExternalPaymentProcessorSample" из сборки "Resto.Front.Api.SamplePaymentPlugin, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null". в Resto.Front.Api.SamplePaymentPlugin.SamplePaymentPlugin..ctor() --- Конец трассировки внутреннего стека исключений --- в System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) в System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) в System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) в System.Activator.CreateInstance(Type type, Boolean nonPublic) в System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark) в System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) в System.Activator.CreateInstanceFromInternal(String assemblyFile, String typeName, Boolean ignoreCase, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, Evidence securityInfo) в Resto.Front.Api.Host.PluginRunner.TryCreatePluginInstance(String assemblyPath, String className) в H:\BuildAgent\work\release-installer\dev\iikoFront.Net\Resto.Front.Api.Host\PluginRunner.cs:строка 149 [2019-08-02 11:16:40,237] ERROR [ 1] - InnerException: System.TypeLoadException: Отсутствует реализация метода "CollectData" в типе "Resto.Front.Api.SamplePaymentPlugin.ExternalPaymentProcessorSample" из сборки "Resto.Front.Api.SamplePaymentPlugin, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null". в Resto.Front.Api.SamplePaymentPlugin.SamplePaymentPlugin..ctor() [2019-08-02 11:16:40,244] INFO [ 1] - Unregistering ipc channel... [2019-08-02 11:16:40,244] INFO [ 1] - Unloading plugin domain... [2019-08-02 11:16:40,248] INFO [ 1] - Shutdown completed

ierof commented 5 years ago

Doc: Управление версиями

Разрабатываемую версию использовать не рекомендуется, поскольку её интерфейс ещё не зафиксирован, обратная совместимость не обеспечивается, возможны любые изменения. Эта версия доступна для изучения нововведений и раннего начала разработки плагина для будущей версии API, чтобы к моменту её выпуска иметь готовый или почти готовый плагин, предоставляющий новые возможности.

Версия API V6 это разрабатываемая версия API. Прогнозируемый срок её выпуска: iiko 7.0. До этого момента по данной версии нет обратной совместимости. Для версии iiko 6.4 рекомендую собрать ваш плагин под API V6Preiview5. Её можно взять из nuget и как вы можете увидеть из схемы в первой ссылке, она будет совместима с версиями iiko 6.4 и 7.0

ierof commented 5 years ago

Конкретно в вашем случае ругается на CollectData потому что в API V6 в iiko 6.3 и iiko 6.4 разные сигнатуры у этого метода интерфейса. Если вы возьмете SDK от iiko 6.4 и попытаетесь собрать свой код, компилятор укажет вам на эту разницу.

Azamatjon commented 5 years ago

@ierof можете ссылку на пример paymentPlugin для iiko 6.3 оставить?

ierof commented 5 years ago

В публичном поле такой ссылки нет. Обратитесь на api@iiko.ru за SDK от версии 6.3.