├── .codecov.yml ├── i18n ├── ar ├── bg ├── cs ├── da ├── de ├── es ├── et ├── fa ├── fr ├── hr ├── hu ├── id ├── it ├── ja ├── ko ├── lv ├── nl ├── no ├── pl ├── pt ├── ro ├── ru ├── sl ├── sr ├── tr ├── uk ├── vi ├── zh ├── pt_BR ├── source.pot ├── no.po ├── sr.po ├── zh.po ├── fa.po ├── ja.po ├── ko.po ├── hr.po ├── et.po ├── nl.po ├── ar.po ├── vi.po ├── id.po ├── de.po ├── da.po ├── lv.po ├── hu.po ├── sl.po ├── es.po ├── tr.po ├── pt.po ├── fr.po ├── it.po ├── pt_BR.po ├── ru.po ├── ro.po ├── cs.po ├── bg.po ├── pl.po └── uk.po ├── .gitignore ├── .tx └── config ├── manifest.php ├── tests ├── phpunit.xml ├── bootstrap.php ├── phpunit-coverage.xml ├── TestHelper.php └── MShop │ └── Service │ └── Provider │ └── Payment │ ├── AuthorizeDPMTest.php │ ├── PayoneTest.php │ ├── AuthorizeSIMTest.php │ ├── StripeTest.php │ ├── NovalnetSepaTest.php │ ├── NovalnetCreditTest.php │ └── DatatransTest.php ├── composer.json ├── src └── MShop │ └── Service │ └── Provider │ └── Payment │ ├── Mpay24.php │ ├── Payone.php │ ├── AuthorizeSIM.php │ ├── AuthorizeDPM.php │ ├── NovalnetSepa.php │ ├── Datatrans.php │ ├── NovalnetCredit.php │ ├── PaypalPlus.php │ └── Stripe.php ├── phing.xml ├── README.md ├── LICENSE └── .circleci └── config.yml /.codecov.yml: -------------------------------------------------------------------------------- 1 | fixes: 2 | - "ext/ai-payments/::" 3 | 4 | -------------------------------------------------------------------------------- /i18n/ar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/ar -------------------------------------------------------------------------------- /i18n/bg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/bg -------------------------------------------------------------------------------- /i18n/cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/cs -------------------------------------------------------------------------------- /i18n/da: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/da -------------------------------------------------------------------------------- /i18n/de: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/de -------------------------------------------------------------------------------- /i18n/es: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/es -------------------------------------------------------------------------------- /i18n/et: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/et -------------------------------------------------------------------------------- /i18n/fa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/fa -------------------------------------------------------------------------------- /i18n/fr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/fr -------------------------------------------------------------------------------- /i18n/hr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/hr -------------------------------------------------------------------------------- /i18n/hu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/hu -------------------------------------------------------------------------------- /i18n/id: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/id -------------------------------------------------------------------------------- /i18n/it: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/it -------------------------------------------------------------------------------- /i18n/ja: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/ja -------------------------------------------------------------------------------- /i18n/ko: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/ko -------------------------------------------------------------------------------- /i18n/lv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/lv -------------------------------------------------------------------------------- /i18n/nl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/nl -------------------------------------------------------------------------------- /i18n/no: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/no -------------------------------------------------------------------------------- /i18n/pl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/pl -------------------------------------------------------------------------------- /i18n/pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/pt -------------------------------------------------------------------------------- /i18n/ro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/ro -------------------------------------------------------------------------------- /i18n/ru: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/ru -------------------------------------------------------------------------------- /i18n/sl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/sl -------------------------------------------------------------------------------- /i18n/sr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/sr -------------------------------------------------------------------------------- /i18n/tr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/tr -------------------------------------------------------------------------------- /i18n/uk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/uk -------------------------------------------------------------------------------- /i18n/vi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/vi -------------------------------------------------------------------------------- /i18n/zh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/zh -------------------------------------------------------------------------------- /i18n/pt_BR: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aimeoscom/ai-payments/HEAD/i18n/pt_BR -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .phpunit.result.cache 2 | .phpunit.cache 3 | .potrans 4 | coveralls.json 5 | coverage.xml 6 | *.log 7 | *.ser 8 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [o:aimeos:p:aimeos-extensions:r:ai-payments] 5 | file_filter = i18n/.po 6 | source_file = i18n/source.pot 7 | source_lang = en 8 | type = PO 9 | -------------------------------------------------------------------------------- /manifest.php: -------------------------------------------------------------------------------- 1 | 'ai-payments', 10 | 'depends' => [ 11 | 'aimeos-core', 12 | ], 13 | 'include' => [ 14 | 'src', 15 | ], 16 | 'i18n' => [ 17 | 'mshop' => 'i18n', 18 | ], 19 | ]; 20 | -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MShop/ 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ../src 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | MShop/ 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aimeos/ai-payments", 3 | "description": "Payment extension for Aimeos e-commerce solutions", 4 | "keywords": ["aimeos", "payment", "shop", "e-commerce", "omnipay", "stripe", "mollie", "authorize", "cardsave", "sagepay"], 5 | "homepage": "https://aimeos.org/", 6 | "type": "aimeos-extension", 7 | "license": "LGPL-3.0-or-later", 8 | "support": { 9 | "source": "https://github.com/aimeos/ai-payments", 10 | "issues": "https://github.com/aimeos/ai-payments/issues", 11 | "forum": "https://aimeos.org/help", 12 | "wiki": "https://aimeos.org/docs" 13 | }, 14 | "prefer-stable": true, 15 | "minimum-stability": "dev", 16 | "require": { 17 | "php": "^8.1", 18 | "aimeos/aimeos-core": "dev-master", 19 | "league/omnipay": "~3.0" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "~10.0||~11.0", 23 | "omnipay/dummy": "~3.0" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "Aimeos\\": "src" 28 | }, 29 | "classmap": [ 30 | "src" 31 | ] 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Aimeos\\": "tests" 36 | }, 37 | "classmap": [ 38 | "tests" 39 | ] 40 | }, 41 | "config": { 42 | "allow-plugins": { 43 | "php-http/discovery": true 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /i18n/source.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #, php-format 21 | msgid "Method \"%1$s\" for provider not available" 22 | msgstr "" 23 | 24 | #, php-format 25 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 26 | msgstr "" 27 | 28 | #, php-format 29 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 30 | msgstr "" 31 | 32 | #, php-format 33 | msgid "No payment token available for customer ID \"%1$s\"" 34 | msgstr "" 35 | 36 | #, php-format 37 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 38 | msgstr "" 39 | 40 | #, php-format 41 | msgid "Order %1$s" 42 | msgstr "" 43 | 44 | msgid "PayPalPlus approval URL not available" 45 | msgstr "" 46 | 47 | msgid "PayPalPlus requires the country ID of the user" 48 | msgstr "" 49 | 50 | #, php-format 51 | msgid "" 52 | "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 53 | msgstr "" 54 | 55 | #, php-format 56 | msgid "Token based payment failed: %1$s" 57 | msgstr "" 58 | 59 | #, php-format 60 | msgid "Token based payment incomplete: %1$s" 61 | msgstr "" 62 | 63 | #, php-format 64 | msgid "Unexpected redirect: %1$s" 65 | msgstr "" 66 | -------------------------------------------------------------------------------- /i18n/no.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: ai-payments\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2025-01-18 15:17+0100\n" 11 | "PO-Revision-Date: 2015-07-20 09:55+0000\n" 12 | "Last-Translator: Aimeos \n" 13 | "Language-Team: Norwegian (http://www.transifex.com/aimeos/ai-payments/" 14 | "language/no/)\n" 15 | "Language: no\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #, php-format 22 | msgid "Method \"%1$s\" for provider not available" 23 | msgstr "" 24 | 25 | #, php-format 26 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 27 | msgstr "" 28 | 29 | #, php-format 30 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 31 | msgstr "" 32 | 33 | #, php-format 34 | msgid "No payment token available for customer ID \"%1$s\"" 35 | msgstr "" 36 | 37 | #, php-format 38 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 39 | msgstr "" 40 | 41 | #, php-format 42 | msgid "Order %1$s" 43 | msgstr "" 44 | 45 | msgid "PayPalPlus approval URL not available" 46 | msgstr "" 47 | 48 | msgid "PayPalPlus requires the country ID of the user" 49 | msgstr "" 50 | 51 | #, php-format 52 | msgid "" 53 | "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 54 | msgstr "" 55 | 56 | #, php-format 57 | msgid "Token based payment failed: %1$s" 58 | msgstr "" 59 | 60 | #, php-format 61 | msgid "Token based payment incomplete: %1$s" 62 | msgstr "" 63 | 64 | #, php-format 65 | msgid "Unexpected redirect: %1$s" 66 | msgstr "" 67 | -------------------------------------------------------------------------------- /i18n/sr.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # 5 | # Translators: 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: ai-payments\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2025-01-18 15:17+0100\n" 11 | "PO-Revision-Date: 2015-07-20 09:55+0000\n" 12 | "Last-Translator: Aimeos \n" 13 | "Language-Team: Serbian (http://www.transifex.com/aimeos/ai-payments/language/" 14 | "sr/)\n" 15 | "Language: sr\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " 20 | "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 21 | 22 | #, php-format 23 | msgid "Method \"%1$s\" for provider not available" 24 | msgstr "" 25 | 26 | #, php-format 27 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 28 | msgstr "" 29 | 30 | #, php-format 31 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 32 | msgstr "" 33 | 34 | #, php-format 35 | msgid "No payment token available for customer ID \"%1$s\"" 36 | msgstr "" 37 | 38 | #, php-format 39 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 40 | msgstr "" 41 | 42 | #, php-format 43 | msgid "Order %1$s" 44 | msgstr "" 45 | 46 | msgid "PayPalPlus approval URL not available" 47 | msgstr "" 48 | 49 | msgid "PayPalPlus requires the country ID of the user" 50 | msgstr "" 51 | 52 | #, php-format 53 | msgid "" 54 | "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 55 | msgstr "" 56 | 57 | #, php-format 58 | msgid "Token based payment failed: %1$s" 59 | msgstr "" 60 | 61 | #, php-format 62 | msgid "Token based payment incomplete: %1$s" 63 | msgstr "" 64 | 65 | #, php-format 66 | msgid "Unexpected redirect: %1$s" 67 | msgstr "" 68 | -------------------------------------------------------------------------------- /i18n/zh.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Hans He, 2022 8 | # Aimeos, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Aimeos, 2025\n" 18 | "Language-Team: Chinese (https://app.transifex.com/aimeos/teams/39015/zh/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: zh\n" 23 | "Plural-Forms: nplurals=1; plural=0;\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "提供商的方法\"%1$s \"不可用" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "客户 ID \"%1$s\" 没有可用的 Stripe 客户数据" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "客户ID \"%1$s\" 无可用的 Stripe 支付方式" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "客户ID \"%1$s\" 无可用的支付令牌" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "客户ID \"%1$s\" 无可用的循环支付数据 " 44 | 45 | #, php-format 46 | msgid "Order %1$s" 47 | msgstr "訂單 %1$s" 48 | 49 | msgid "PayPalPlus approval URL not available" 50 | msgstr "PayPalPlus审批URL不可用" 51 | 52 | msgid "PayPalPlus requires the country ID of the user" 53 | msgstr "PayPalPlus 需要用户的国家 ID" 54 | 55 | #, php-format 56 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 57 | msgstr "使用令牌支付失败,代码 \"%1$s\" 消息 \"%2$s\": %3$s" 58 | 59 | #, php-format 60 | msgid "Token based payment failed: %1$s" 61 | msgstr "基于代币的支付失败: %1$s" 62 | 63 | #, php-format 64 | msgid "Token based payment incomplete: %1$s" 65 | msgstr "基于代币的支付未完成: %1$s" 66 | 67 | #, php-format 68 | msgid "Unexpected redirect: %1$s" 69 | msgstr "未預期的重新導向: %1$s" 70 | -------------------------------------------------------------------------------- /tests/TestHelper.php: -------------------------------------------------------------------------------- 1 | getIncludePaths(); 18 | $includepaths[] = get_include_path(); 19 | set_include_path( implode( PATH_SEPARATOR, $includepaths ) ); 20 | } 21 | 22 | 23 | public static function getAimeos() 24 | { 25 | if( !isset( self::$aimeos ) ) 26 | { 27 | require_once 'Bootstrap.php'; 28 | spl_autoload_register( 'Aimeos\\Bootstrap::autoload' ); 29 | 30 | self::$aimeos = new \Aimeos\Bootstrap(); 31 | } 32 | 33 | return self::$aimeos; 34 | } 35 | 36 | 37 | public static function context( $site = 'unittest' ) 38 | { 39 | if( !isset( self::$context[$site] ) ) { 40 | self::$context[$site] = self::createContext( $site ); 41 | } 42 | 43 | return clone self::$context[$site]; 44 | } 45 | 46 | 47 | private static function createContext( $site ) 48 | { 49 | $ctx = new \Aimeos\MShop\Context(); 50 | $aimeos = self::getAimeos(); 51 | 52 | 53 | $paths = $aimeos->getConfigPaths( 'mysql' ); 54 | $paths[] = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'config'; 55 | 56 | $conf = new \Aimeos\Base\Config\PHPArray( [], $paths ); 57 | $ctx->setConfig( $conf ); 58 | 59 | 60 | $dbm = new \Aimeos\Base\DB\Manager\Standard( $conf->get( 'resource', [] ), 'PDO' ); 61 | $ctx->setDatabaseManager( $dbm ); 62 | 63 | 64 | $logger = new \Aimeos\Base\Logger\File( $site . '.log', \Aimeos\Base\Logger\Iface::DEBUG ); 65 | $ctx->setLogger( $logger ); 66 | 67 | 68 | $i18n = new \Aimeos\Base\Translation\None( 'de' ); 69 | $ctx->setI18n( array( 'de' => $i18n ) ); 70 | 71 | 72 | $session = new \Aimeos\Base\Session\None(); 73 | $ctx->setSession( $session ); 74 | 75 | 76 | $localeManager = \Aimeos\MShop::create( $ctx, 'locale' ); 77 | $localeItem = $localeManager->bootstrap( $site, '', '', false ); 78 | 79 | $ctx->setLocale( $localeItem ); 80 | 81 | $ctx->setEditor( 'ai-payments' ); 82 | 83 | return $ctx; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /i18n/fa.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2022 8 | # hasan hojjati , 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: hasan hojjati , 2025\n" 18 | "Language-Team: Persian (https://app.transifex.com/aimeos/teams/39015/fa/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: fa\n" 23 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "متد \"%1$s\" برای فراهم کننده موجود نیست" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "برای مشتری با شناسه \"%1$s\" هیچ توکن پرداختی موجود نیست" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "اطلاعات پرداخت دوباره ای برای مشتری با شناسه \"%1$s\" موجود نیست" 44 | 45 | #, php-format 46 | msgid "Order %1$s" 47 | msgstr "سفارش %1$s" 48 | 49 | msgid "PayPalPlus approval URL not available" 50 | msgstr "" 51 | 52 | msgid "PayPalPlus requires the country ID of the user" 53 | msgstr "" 54 | 55 | #, php-format 56 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 57 | msgstr "" 58 | 59 | #, php-format 60 | msgid "Token based payment failed: %1$s" 61 | msgstr "پرداخت بر پایه توکن ناموفق:%1$s" 62 | 63 | #, php-format 64 | msgid "Token based payment incomplete: %1$s" 65 | msgstr "" 66 | 67 | #, php-format 68 | msgid "Unexpected redirect: %1$s" 69 | msgstr "تغییر مسیر غیرمنتظره:%1$s" 70 | -------------------------------------------------------------------------------- /i18n/ja.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Japanese (https://app.transifex.com/aimeos/teams/39015/ja/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: ja\n" 22 | "Plural-Forms: nplurals=1; plural=0;\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "プロバイダのメソッド \"%1$s\" が利用できません。" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "顧客ID \"%1$s\" に利用可能なStripe顧客データがありません。" 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "顧客ID \"%1$s \"に利用可能なStripe支払い方法がありません。" 35 | 36 | #, php-format 37 | msgid "No payment token available for customer ID \"%1$s\"" 38 | msgstr "顧客 ID \"%1$s\" に利用可能な支払トークンがありません。" 39 | 40 | #, php-format 41 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 42 | msgstr "顧客 ID \"%1$s\" に利用可能な定期支払データがありません。" 43 | 44 | #, php-format 45 | msgid "Order %1$s" 46 | msgstr "注文 %1$s" 47 | 48 | msgid "PayPalPlus approval URL not available" 49 | msgstr "PayPalPlusの承認URLが利用できません" 50 | 51 | msgid "PayPalPlus requires the country ID of the user" 52 | msgstr "PayPalPlusは、ユーザーの国IDを必要とします。" 53 | 54 | #, php-format 55 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 56 | msgstr "トークン・ベースの支払いはコード \"%1$s\" とメッセージ \"%2$s\" で失敗しました:%3$s" 57 | 58 | #, php-format 59 | msgid "Token based payment failed: %1$s" 60 | msgstr "トークンによる支払いに失敗しました: %1$s" 61 | 62 | #, php-format 63 | msgid "Token based payment incomplete: %1$s" 64 | msgstr "トークン・ベースの支払いが不完全です: %1$s" 65 | 66 | #, php-format 67 | msgid "Unexpected redirect: %1$s" 68 | msgstr "予期しないリダイレクト: %1$s" 69 | -------------------------------------------------------------------------------- /i18n/ko.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Korean (https://app.transifex.com/aimeos/teams/39015/ko/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: ko\n" 22 | "Plural-Forms: nplurals=1; plural=0;\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "공급자에 대한 메서드 \"%1$s\"를 사용할 수 없습니다." 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "고객 ID \"%1$s\"에 사용할 수 있는 스트라이프 고객 데이터가 없습니다." 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "고객 ID \"%1$s\"에 사용할 수 있는 스트라이프 결제 방법이 없습니다." 35 | 36 | #, php-format 37 | msgid "No payment token available for customer ID \"%1$s\"" 38 | msgstr "고객 ID \"%1$s\"에 사용할 수 있는 결제 토큰이 없습니다." 39 | 40 | #, php-format 41 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 42 | msgstr "고객 ID \"%1$s\"에 사용할 수 있는 반복 결제 데이터가 없습니다." 43 | 44 | #, php-format 45 | msgid "Order %1$s" 46 | msgstr "주문 %1$s" 47 | 48 | msgid "PayPalPlus approval URL not available" 49 | msgstr "페이팔플러스 승인 URL을 사용할 수 없습니다." 50 | 51 | msgid "PayPalPlus requires the country ID of the user" 52 | msgstr "PayPalPlus에는 사용자의 국가 ID가 필요합니다." 53 | 54 | #, php-format 55 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 56 | msgstr "토큰 기반 결제에 실패했습니다(코드 \"%1$s\" 및 메시지 \"%2$s\"): %3$s" 57 | 58 | #, php-format 59 | msgid "Token based payment failed: %1$s" 60 | msgstr "토큰 기반 결제가 실패했습니다: %1$s" 61 | 62 | #, php-format 63 | msgid "Token based payment incomplete: %1$s" 64 | msgstr "토큰 기반 결제가 완료되지 않았습니다: %1$s" 65 | 66 | #, php-format 67 | msgid "Unexpected redirect: %1$s" 68 | msgstr "예기치 않은 리디렉션: %1$s" 69 | -------------------------------------------------------------------------------- /i18n/hr.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2022 8 | # Miro Sertić , 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Miro Sertić , 2025\n" 18 | "Language-Team: Croatian (https://app.transifex.com/aimeos/teams/39015/hr/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: hr\n" 23 | "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "Metoda \"%1$s\" za davatelja usluga nije dostupna" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Nije dostupan token za plaćanje za korisnički ID \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "" 44 | "Nema podataka o ponovnom pojavljivanju podataka o plaćanju za korisnički ID " 45 | "\"%1$s\"" 46 | 47 | #, php-format 48 | msgid "Order %1$s" 49 | msgstr "Narudžba %1$s" 50 | 51 | msgid "PayPalPlus approval URL not available" 52 | msgstr "" 53 | 54 | msgid "PayPalPlus requires the country ID of the user" 55 | msgstr "" 56 | 57 | #, php-format 58 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 59 | msgstr "" 60 | 61 | #, php-format 62 | msgid "Token based payment failed: %1$s" 63 | msgstr "Plaćanje na temelju tokena nije uspjelo:%1$s" 64 | 65 | #, php-format 66 | msgid "Token based payment incomplete: %1$s" 67 | msgstr "" 68 | 69 | #, php-format 70 | msgid "Unexpected redirect: %1$s" 71 | msgstr "Neočekivano preusmjeravanje: %1$s" 72 | -------------------------------------------------------------------------------- /i18n/et.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Estonian (https://app.transifex.com/aimeos/teams/39015/et/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: et\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "Meetod \"%1$s\" teenusepakkuja jaoks ei ole saadaval" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "Kliendi ID \"%1$s\" jaoks ei ole Stripe'i kliendiandmeid saadaval." 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "Kliendi ID \"%1$s\" jaoks pole Stripe'i makseviis saadaval." 35 | 36 | #, php-format 37 | msgid "No payment token available for customer ID \"%1$s\"" 38 | msgstr "Kliendi ID \"%1$s\" jaoks ei ole maksetähist saadaval." 39 | 40 | #, php-format 41 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 42 | msgstr "Kliendi ID \"%1$s\" kohta ei ole korduvmakse andmeid saadaval." 43 | 44 | #, php-format 45 | msgid "Order %1$s" 46 | msgstr "Tellimine %1$s" 47 | 48 | msgid "PayPalPlus approval URL not available" 49 | msgstr "PayPalPlus heakskiitmise URL ei ole saadaval" 50 | 51 | msgid "PayPalPlus requires the country ID of the user" 52 | msgstr "PayPalPlus nõuab kasutaja riigi ID-d" 53 | 54 | #, php-format 55 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 56 | msgstr "Tokenipõhine makse ebaõnnestus koodiga \"%1$s\" ja sõnumiga \"%2$s\": %3$s" 57 | 58 | #, php-format 59 | msgid "Token based payment failed: %1$s" 60 | msgstr "Tokenipõhine makse ebaõnnestus: %1$s" 61 | 62 | #, php-format 63 | msgid "Token based payment incomplete: %1$s" 64 | msgstr "Tokenipõhine makse on mittetäielik: %1$s" 65 | 66 | #, php-format 67 | msgid "Unexpected redirect: %1$s" 68 | msgstr "Ootamatu ümbersuunamine: %1$s" 69 | -------------------------------------------------------------------------------- /i18n/nl.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Dutch (https://app.transifex.com/aimeos/teams/39015/nl/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: nl\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "Methode \"%1$s\" voor provider niet beschikbaar" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "Geen Stripe-klantgegevens beschikbaar voor klant-ID \"%1$s\"." 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "Geen Stripe-betalingsmethode beschikbaar voor klant-ID \"%1$s\"." 35 | 36 | #, php-format 37 | msgid "No payment token available for customer ID \"%1$s\"" 38 | msgstr "Geen betalingstoken beschikbaar voor klant-ID \"%1$s\"." 39 | 40 | #, php-format 41 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 42 | msgstr "Geen terugkerende betalingsgegevens beschikbaar voor klant-ID \"%1$s\"." 43 | 44 | #, php-format 45 | msgid "Order %1$s" 46 | msgstr "Bestelling %1$s" 47 | 48 | msgid "PayPalPlus approval URL not available" 49 | msgstr "PayPalPlus goedkeuring URL niet beschikbaar" 50 | 51 | msgid "PayPalPlus requires the country ID of the user" 52 | msgstr "PayPalPlus heeft de land-ID van de gebruiker nodig" 53 | 54 | #, php-format 55 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 56 | msgstr "Betaling met token is mislukt met code \"%1$s\" en bericht \"%2$s\": %3$s" 57 | 58 | #, php-format 59 | msgid "Token based payment failed: %1$s" 60 | msgstr "Betaling met token mislukt: %1$s" 61 | 62 | #, php-format 63 | msgid "Token based payment incomplete: %1$s" 64 | msgstr "Token-gebaseerde betaling onvolledig: %1$s" 65 | 66 | #, php-format 67 | msgid "Unexpected redirect: %1$s" 68 | msgstr "Onverwachte doorverwijzing: %1$s" 69 | -------------------------------------------------------------------------------- /i18n/ar.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Arabic (https://app.transifex.com/aimeos/teams/39015/ar/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: ar\n" 22 | "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "الطريقة \"%1$s\" للموفر غير متوفرة" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "لا تتوفر بيانات عميل Stripe لمعرف العميل \"%1$s\"" 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "لا تتوفر طريقة دفع Stripe لمعرف العميل \"%1$s\"" 35 | 36 | #, php-format 37 | msgid "No payment token available for customer ID \"%1$s\"" 38 | msgstr "لا يتوفر رمز دفع مميز لمعرف العميل \"%1$s\"" 39 | 40 | #, php-format 41 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 42 | msgstr "لا توجد بيانات دفع متكررة متوفرة لمعرف العميل \"%1$s\"" 43 | 44 | #, php-format 45 | msgid "Order %1$s" 46 | msgstr "الطلب %1$s" 47 | 48 | msgid "PayPalPlus approval URL not available" 49 | msgstr "عنوان URL الموافقة على PayPalPlPlus غير متاح" 50 | 51 | msgid "PayPalPlus requires the country ID of the user" 52 | msgstr "يتطلب PayPalPlPlus مُعرِّف البلد الخاص بالمستخدم" 53 | 54 | #, php-format 55 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 56 | msgstr "فشل الدفع المستند إلى الرمز المميز بالرمز \"%1$s\" والرسالة \"%2$s\": %3$s" 57 | 58 | #, php-format 59 | msgid "Token based payment failed: %1$s" 60 | msgstr "فشل الدفع المستند إلى الرمز المميز: %1$s" 61 | 62 | #, php-format 63 | msgid "Token based payment incomplete: %1$s" 64 | msgstr "الدفع على أساس الرمز المميز غير مكتمل: %1$s" 65 | 66 | #, php-format 67 | msgid "Unexpected redirect: %1$s" 68 | msgstr "إعادة توجيه غير متوقعة: %1$s" 69 | -------------------------------------------------------------------------------- /i18n/vi.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Vietnamese (https://app.transifex.com/aimeos/teams/39015/vi/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: vi\n" 22 | "Plural-Forms: nplurals=1; plural=0;\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "Phương thức \"%1$s\" không khả dụng cho nhà cung cấp." 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "Không có dữ liệu khách hàng của Stripe cho ID khách hàng \"%1$s\"" 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "" 35 | "Không có phương thức thanh toán Stripe khả dụng cho ID khách hàng \"%1$s\"" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Không có mã thanh toán khả dụng cho ID khách hàng \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "Không có dữ liệu thanh toán định kỳ cho ID khách hàng \"%1$s\"" 44 | 45 | #, php-format 46 | msgid "Order %1$s" 47 | msgstr "Đặt hàng %1$s" 48 | 49 | msgid "PayPalPlus approval URL not available" 50 | msgstr "URL phê duyệt PayPalPlus không khả dụng" 51 | 52 | msgid "PayPalPlus requires the country ID of the user" 53 | msgstr "PayPalPlus yêu cầu mã quốc gia của người dùng." 54 | 55 | #, php-format 56 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 57 | msgstr "Thanh toán bằng token đã thất bại với mã \"%1$s\" và thông báo \"%2$s\": %3$s" 58 | 59 | #, php-format 60 | msgid "Token based payment failed: %1$s" 61 | msgstr "Thanh toán bằng token không thành công: %1$s" 62 | 63 | #, php-format 64 | msgid "Token based payment incomplete: %1$s" 65 | msgstr "Thanh toán bằng token chưa hoàn tất: %1$s" 66 | 67 | #, php-format 68 | msgid "Unexpected redirect: %1$s" 69 | msgstr "Chuyển hướng không mong muốn: %1$s" 70 | -------------------------------------------------------------------------------- /i18n/id.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Indonesian (https://app.transifex.com/aimeos/teams/39015/id/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: id\n" 22 | "Plural-Forms: nplurals=1; plural=0;\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "Metode \"%1$s\" untuk penyedia tidak tersedia" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "Tidak ada data pelanggan Stripe yang tersedia untuk ID pelanggan \"%1$s\"" 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "" 35 | "Tidak ada metode pembayaran Stripe yang tersedia untuk ID pelanggan \"%1$s\"" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Tidak ada token pembayaran yang tersedia untuk ID pelanggan \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "" 44 | "Tidak ada data pembayaran berulang yang tersedia untuk ID pelanggan \"%1$s\"" 45 | 46 | #, php-format 47 | msgid "Order %1$s" 48 | msgstr "Pesan %1$s" 49 | 50 | msgid "PayPalPlus approval URL not available" 51 | msgstr "URL persetujuan PayPalPlus tidak tersedia" 52 | 53 | msgid "PayPalPlus requires the country ID of the user" 54 | msgstr "PayPalPlus memerlukan ID negara pengguna" 55 | 56 | #, php-format 57 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 58 | msgstr "Pembayaran berbasis token gagal dengan kode \"%1$s\" dan pesan \"%2$s\": %3$s" 59 | 60 | #, php-format 61 | msgid "Token based payment failed: %1$s" 62 | msgstr "Pembayaran berbasis token gagal: %1$s" 63 | 64 | #, php-format 65 | msgid "Token based payment incomplete: %1$s" 66 | msgstr "Pembayaran berbasis token tidak lengkap: %1$s" 67 | 68 | #, php-format 69 | msgid "Unexpected redirect: %1$s" 70 | msgstr "Pengalihan tak terduga: %1$s" 71 | -------------------------------------------------------------------------------- /i18n/de.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: German (https://app.transifex.com/aimeos/teams/39015/de/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: de\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "Methode \"%1$s\" ist nicht verfügbar" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "Keine Stripe Kundendaten für die Kunden-ID \"%1$s\" verfügbar" 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "Keine Stripe Zahlungsmethode für die Kunden-ID \"%1$s\" verfügbar" 35 | 36 | #, php-format 37 | msgid "No payment token available for customer ID \"%1$s\"" 38 | msgstr "Kein Zahlungstoken für den Kunden mit der ID \"%1$s\" verfügbar" 39 | 40 | #, php-format 41 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 42 | msgstr "Keine Zahlungsreferenz für den Kunden mit der ID \"%1$s\" verfügbar" 43 | 44 | #, php-format 45 | msgid "Order %1$s" 46 | msgstr "Bestellung %1$s" 47 | 48 | msgid "PayPalPlus approval URL not available" 49 | msgstr "Bestätigungs-URL für PayPalPlus ist nicht verfügbar" 50 | 51 | msgid "PayPalPlus requires the country ID of the user" 52 | msgstr "PayPalPlus benötigt den Ländercode des Kunden" 53 | 54 | #, php-format 55 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 56 | msgstr "" 57 | "Token-basierte Zahlung ist mit dem Code \"%1$s\" und Fehlermeldung \"%2$s\" " 58 | "ist fehlgeschlagen: %3$s" 59 | 60 | #, php-format 61 | msgid "Token based payment failed: %1$s" 62 | msgstr "Token-basierte Zahlung ist fehlgeschlagen: %1$s" 63 | 64 | #, php-format 65 | msgid "Token based payment incomplete: %1$s" 66 | msgstr "Token-basierte Zahlung unvollständig: %1$s" 67 | 68 | #, php-format 69 | msgid "Unexpected redirect: %1$s" 70 | msgstr "Unerwartete Weiterleitung: %1$s" 71 | -------------------------------------------------------------------------------- /i18n/da.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Simon Boye , 2025 8 | # Aimeos, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Aimeos, 2025\n" 18 | "Language-Team: Danish (https://app.transifex.com/aimeos/teams/39015/da/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: da\n" 23 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "Metoden \"%1$s\" for udbyder er ikke tilgængelig" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "Ingen Stripe-kundedata tilgængelige for kunde-ID \"%1$s\"" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "Ingen Stripe-betalingsmetode tilgængelig for kunde-ID \"%1$s\"" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Intet betalingstoken tilgængeligt for kunde-ID \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "" 44 | "Ingen data om tilbagevendende betalinger tilgængelige for kunde-ID \"%1$s\"" 45 | 46 | #, php-format 47 | msgid "Order %1$s" 48 | msgstr "Ordre %1$s" 49 | 50 | msgid "PayPalPlus approval URL not available" 51 | msgstr "URL til PayPalPlus-godkendelse ikke tilgængelig" 52 | 53 | msgid "PayPalPlus requires the country ID of the user" 54 | msgstr "PayPalPlus kræver brugerens lande-ID" 55 | 56 | #, php-format 57 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 58 | msgstr "" 59 | "Tokenbaseret betaling mislykkedes med koden \"%1$s\" og meddelelsen " 60 | "\"%2$s\": %3$s" 61 | 62 | #, php-format 63 | msgid "Token based payment failed: %1$s" 64 | msgstr "Token-baseret betaling mislykkedes: %1$s" 65 | 66 | #, php-format 67 | msgid "Token based payment incomplete: %1$s" 68 | msgstr "Token-baseret betaling ufuldstændig: %1$s" 69 | 70 | #, php-format 71 | msgid "Unexpected redirect: %1$s" 72 | msgstr "Uforudset redirect: %1$s" 73 | -------------------------------------------------------------------------------- /i18n/lv.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Latvian (https://app.transifex.com/aimeos/teams/39015/lv/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: lv\n" 22 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "Metode \"%1$s\" pakalpojumu sniedzējam nav pieejama" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "Par klienta ID \"%1$s\" nav pieejami Stripe klienta dati" 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "Klientam ar ID \"%1$s\" nav pieejama neviena Stripe maksājumu metode." 35 | 36 | #, php-format 37 | msgid "No payment token available for customer ID \"%1$s\"" 38 | msgstr "Klienta ID \"%1$s\" nav pieejams neviens maksājuma žetons." 39 | 40 | #, php-format 41 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 42 | msgstr "Par klienta ID \"%1$s\" nav pieejami atkārtotu maksājumu dati." 43 | 44 | #, php-format 45 | msgid "Order %1$s" 46 | msgstr "Pasūtījums %1$s" 47 | 48 | msgid "PayPalPlus approval URL not available" 49 | msgstr "PayPalPlus apstiprināšanas URL nav pieejams" 50 | 51 | msgid "PayPalPlus requires the country ID of the user" 52 | msgstr "PayPalPlus pieprasa lietotāja valsts ID" 53 | 54 | #, php-format 55 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 56 | msgstr "" 57 | "Uz žetonu balstīts maksājums neizdevās ar kodu \"%1$s\" un ziņojumu " 58 | "\"%2$s\": %3$s" 59 | 60 | #, php-format 61 | msgid "Token based payment failed: %1$s" 62 | msgstr "Uz žetonu balstīts maksājums neizdevās: %1$s" 63 | 64 | #, php-format 65 | msgid "Token based payment incomplete: %1$s" 66 | msgstr "Nepilnīgs uz žetoniem balstīts maksājums: %1$s" 67 | 68 | #, php-format 69 | msgid "Unexpected redirect: %1$s" 70 | msgstr "Negaidīts pāradresējums: %1$s" 71 | -------------------------------------------------------------------------------- /i18n/hu.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Hungarian (https://app.transifex.com/aimeos/teams/39015/hu/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: hu\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "\"%1$s\" módszer a szolgáltatóhoz nem elérhető" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "" 31 | "A \"%1$s\" ügyfél azonosítóhoz nem állnak rendelkezésre Stripe ügyféladatok." 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "Nincs elérhető Stripe fizetési mód a \"%1$s\" ügyfél azonosítóhoz." 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Nincs elérhető fizetési token a \"%1$s\" ügyfél azonosítóhoz." 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "" 44 | "A \"%1$s\" ügyfél azonosítóhoz nem állnak rendelkezésre ismétlődő fizetési " 45 | "adatok." 46 | 47 | #, php-format 48 | msgid "Order %1$s" 49 | msgstr "Megrendelés %1$s" 50 | 51 | msgid "PayPalPlus approval URL not available" 52 | msgstr "PayPalPlus jóváhagyási URL nem elérhető" 53 | 54 | msgid "PayPalPlus requires the country ID of the user" 55 | msgstr "A PayPalPlus megköveteli a felhasználó országazonosítóját" 56 | 57 | #, php-format 58 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 59 | msgstr "" 60 | "A token alapú fizetés sikertelen volt \"%1$s\" kóddal és \"%2$s\" üzenettel:" 61 | " %3$s" 62 | 63 | #, php-format 64 | msgid "Token based payment failed: %1$s" 65 | msgstr "Token alapú fizetés sikertelen: %1$s" 66 | 67 | #, php-format 68 | msgid "Token based payment incomplete: %1$s" 69 | msgstr "Token alapú fizetés nem teljes: %1$s" 70 | 71 | #, php-format 72 | msgid "Unexpected redirect: %1$s" 73 | msgstr "Váratlan átirányítás: %1$s" 74 | -------------------------------------------------------------------------------- /i18n/sl.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Slovenian (https://app.transifex.com/aimeos/teams/39015/sl/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: sl\n" 22 | "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "Metoda \"%1$s\" za ponudnika ni na voljo" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "Za ID stranke \"%1$s\" ni na voljo podatkov o stranki Stripe." 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "Za ID stranke \"%1$s\" ni na voljo nobene plačilne metode Stripe." 35 | 36 | #, php-format 37 | msgid "No payment token available for customer ID \"%1$s\"" 38 | msgstr "Za ID stranke \"%1$s\" ni na voljo nobenega plačilnega žetona." 39 | 40 | #, php-format 41 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 42 | msgstr "Za ID stranke \"%1$s\" ni na voljo podatkov o ponavljajočih se plačilih." 43 | 44 | #, php-format 45 | msgid "Order %1$s" 46 | msgstr "Naročilo %1$s" 47 | 48 | msgid "PayPalPlus approval URL not available" 49 | msgstr "URL za odobritev storitve PayPalPlus ni na voljo" 50 | 51 | msgid "PayPalPlus requires the country ID of the user" 52 | msgstr "PayPalPlus zahteva ID države uporabnika" 53 | 54 | #, php-format 55 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 56 | msgstr "" 57 | "Plačilo na podlagi žetona ni uspelo s kodo \"%1$s\" in sporočilom \"%2$s\": " 58 | "%3$s" 59 | 60 | #, php-format 61 | msgid "Token based payment failed: %1$s" 62 | msgstr "Plačilo na podlagi žetona ni uspelo: %1$s" 63 | 64 | #, php-format 65 | msgid "Token based payment incomplete: %1$s" 66 | msgstr "Plačilo na podlagi žetona ni dokončano: %1$s" 67 | 68 | #, php-format 69 | msgid "Unexpected redirect: %1$s" 70 | msgstr "Nepričakovana preusmeritev: %1$s" 71 | -------------------------------------------------------------------------------- /i18n/es.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Lara Merlotto , 2025 8 | # Aimeos, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Aimeos, 2025\n" 18 | "Language-Team: Spanish (https://app.transifex.com/aimeos/teams/39015/es/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: es\n" 23 | "Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "Método \"%1$s\" para el proveedor no disponible" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "Ningúna linea de datos del cliente disponible para el cliente ID \"%1$s\"" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "Ningúna línea de método de pago disponible para el cliente ID \"%1$s\"" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Ningún token de pago disponible para el cliente ID \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "Ningún dato de pago recurrente disponible para el cliente ID \"%1$s\"" 44 | 45 | #, php-format 46 | msgid "Order %1$s" 47 | msgstr "Orden %1$s" 48 | 49 | msgid "PayPalPlus approval URL not available" 50 | msgstr "URL de aprobación de PayPalPlus no disponible" 51 | 52 | msgid "PayPalPlus requires the country ID of the user" 53 | msgstr "PayPalPlus requiere el identificador de país del usuario" 54 | 55 | #, php-format 56 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 57 | msgstr "Pago basado con el Token fallido con código \"%1$s\" y mensaje \"%2$s\".%3$s" 58 | 59 | #, php-format 60 | msgid "Token based payment failed: %1$s" 61 | msgstr "Pago basado con el Token fallido: %1$s" 62 | 63 | #, php-format 64 | msgid "Token based payment incomplete: %1$s" 65 | msgstr "Pago basado en token incompleto: %1$s" 66 | 67 | #, php-format 68 | msgid "Unexpected redirect: %1$s" 69 | msgstr "Redirección no esperada: %1$s" 70 | -------------------------------------------------------------------------------- /i18n/tr.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # Metal , 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Metal , 2025\n" 18 | "Language-Team: Turkish (https://app.transifex.com/aimeos/teams/39015/tr/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: tr\n" 23 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "Sağlayıcı için yöntem \"%1$s\" mevcut değil" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "\"%1$s\" müşteri kimliği için hiçbir Stripe müşteri bilgisi mevcut değil" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "\"%1$s\" müşteri kimliği için hiçbir Stripe ödeme yöntemi mevcut değil" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "\"%1$s\" müşteri kimliği için herhangi bir ödeme belirteci yok" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "\"%1$s\" müşteri kimliği için herhangi bir yinelenen ödeme verisi yok" 44 | 45 | #, php-format 46 | msgid "Order %1$s" 47 | msgstr "Sipariş %1$s" 48 | 49 | msgid "PayPalPlus approval URL not available" 50 | msgstr "PayPalPlus onay URL'si mevcut değil" 51 | 52 | msgid "PayPalPlus requires the country ID of the user" 53 | msgstr "PayPal Plus, kullanıcının ülke kimlik numarasını (ID) gerektirir" 54 | 55 | #, php-format 56 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 57 | msgstr "" 58 | "Token tabanlı ödeme \"%1$s\" koduyla ve \"%2$s\" mesajıyla başarısız oldu: " 59 | "%3$s" 60 | 61 | #, php-format 62 | msgid "Token based payment failed: %1$s" 63 | msgstr "Token-bazlı ödeme başarısız oldu: %1$s" 64 | 65 | #, php-format 66 | msgid "Token based payment incomplete: %1$s" 67 | msgstr "Token tabanlı ödeme tamamlanmadı: %1$s" 68 | 69 | #, php-format 70 | msgid "Unexpected redirect: %1$s" 71 | msgstr "Beklenmeyen yönlendirme: %1$s" 72 | -------------------------------------------------------------------------------- /i18n/pt.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Antonio Sequeira , 2022 8 | # Aimeos, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Aimeos, 2025\n" 18 | "Language-Team: Portuguese (https://app.transifex.com/aimeos/teams/39015/pt/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: pt\n" 23 | "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "Método \"%1$s\" para o fornecedor não disponível" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "Não existem dados de cliente na Stripe para o ID \"%1$s\"" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "Não existe método de pagamento disponível para o ID \"%1$s\"" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Não existe token de pagamento disponível para o cliente ID \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "Não existem dados de pagamento recorrente para o cliente ID \"%1$s\"" 44 | 45 | #, php-format 46 | msgid "Order %1$s" 47 | msgstr "Pedido %1$s" 48 | 49 | msgid "PayPalPlus approval URL not available" 50 | msgstr "URL de aprovação do PayPalPlus não disponível" 51 | 52 | msgid "PayPalPlus requires the country ID of the user" 53 | msgstr "O PayPalPlus requer o ID do país do utilizador" 54 | 55 | #, php-format 56 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 57 | msgstr "" 58 | "Pagamento baseado em tocken falhou com código \"%1$s\" e mensagem \"%2$s\": " 59 | "%3$s" 60 | 61 | #, php-format 62 | msgid "Token based payment failed: %1$s" 63 | msgstr "Falha no pagamento baseado em token: %1$s" 64 | 65 | #, php-format 66 | msgid "Token based payment incomplete: %1$s" 67 | msgstr "Pagamento baseado em token incompleto: %1$s" 68 | 69 | #, php-format 70 | msgid "Unexpected redirect: %1$s" 71 | msgstr "Redirecionamento inesperado: %1$s" 72 | -------------------------------------------------------------------------------- /i18n/fr.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # a270031086f2a0d3514bc0cb507b48f6, 2025 8 | # Aimeos, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Aimeos, 2025\n" 18 | "Language-Team: French (https://app.transifex.com/aimeos/teams/39015/fr/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: fr\n" 23 | "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "Méthode \"%1$s\" pour fournisseur non disponible" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "Aucune donnée client Stripe disponible pour l'ID client \"%1$s\"" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "" 36 | "Aucune méthode de paiement Stripe n'est disponible pour l'ID du client " 37 | "\"%1$s\"" 38 | 39 | #, php-format 40 | msgid "No payment token available for customer ID \"%1$s\"" 41 | msgstr "Aucun jeton de paiement disponible pour l'ID client \"%1$s\"" 42 | 43 | #, php-format 44 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 45 | msgstr "Aucune donnée de paiement récurrent disponible pour l'ID client \"%1$s\"" 46 | 47 | #, php-format 48 | msgid "Order %1$s" 49 | msgstr "Ordre %1$s" 50 | 51 | msgid "PayPalPlus approval URL not available" 52 | msgstr "L'URL d'approbation de PayPalPlus n'est pas disponible" 53 | 54 | msgid "PayPalPlus requires the country ID of the user" 55 | msgstr "PayPalPlus demande l'identifiant du pays de l'utilisateur." 56 | 57 | #, php-format 58 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 59 | msgstr "" 60 | "Le paiement par jeton a échoué avec le code \"%1$s\" et le message \"%2$s\" " 61 | ": %3$s" 62 | 63 | #, php-format 64 | msgid "Token based payment failed: %1$s" 65 | msgstr "Le paiement basé sur un jeton a échoué:%1$s" 66 | 67 | #, php-format 68 | msgid "Token based payment incomplete: %1$s" 69 | msgstr "Paiement par jeton incomplet : %1$s" 70 | 71 | #, php-format 72 | msgid "Unexpected redirect: %1$s" 73 | msgstr "Redirection inattendue: %1$s" 74 | -------------------------------------------------------------------------------- /i18n/it.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # isabella carlin , 2025 8 | # Aimeos, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Aimeos, 2025\n" 18 | "Language-Team: Italian (https://app.transifex.com/aimeos/teams/39015/it/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: it\n" 23 | "Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "Metodo %1$s non disponibile per il fornitore " 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "Nessuna sezione dati clienti disponibile per l'ID cliente \"%1$s\"" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "Nessuna sezione metodo di pagamento disponibile per l'ID cliente \"%1$s\"" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Nessun token di pagamento disponibile per l'ID cliente \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "" 44 | "Non sono disponibili dati di pagamento ricorrenti per l'ID cliente \"%1$s\"" 45 | 46 | #, php-format 47 | msgid "Order %1$s" 48 | msgstr "Ordine %1$s" 49 | 50 | msgid "PayPalPlus approval URL not available" 51 | msgstr "URL di approvazione PayPalPlus non disponibile" 52 | 53 | msgid "PayPalPlus requires the country ID of the user" 54 | msgstr "PayPalPlus richiede l'ID del paese dell'utente" 55 | 56 | #, php-format 57 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 58 | msgstr "" 59 | "Il pagamento basato su token con codice \"%1$s\" e messaggio \"%2$s\" non è " 60 | "andato a buon fine:%3$s " 61 | 62 | #, php-format 63 | msgid "Token based payment failed: %1$s" 64 | msgstr "Il pagamento basato su token non è andato a buon fine: %1$s" 65 | 66 | #, php-format 67 | msgid "Token based payment incomplete: %1$s" 68 | msgstr "Pagamento basato su token incompleto: %1$s" 69 | 70 | #, php-format 71 | msgid "Unexpected redirect: %1$s" 72 | msgstr "Redirect inaspettato: %1$s" 73 | -------------------------------------------------------------------------------- /i18n/pt_BR.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Keite Clembet, 2022 8 | # Aimeos, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Aimeos, 2025\n" 18 | "Language-Team: Portuguese (Brazil) (https://app.transifex.com/aimeos/teams/39015/pt_BR/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: pt_BR\n" 23 | "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "O método \"%1$s\" para o provedor não está disponível" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "Nenhum dado de cliente do Stripe disponível para o ID de cliente \"%1$s\"" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "Método de pagamento Stripe não disponível para o ID de cliente \"%1$s\"" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Nenhum token de pagamento disponível para o ID de cliente \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "" 44 | "Nenhum dado de pagamento recorrente disponível para o ID de cliente \"%1$s\"" 45 | 46 | #, php-format 47 | msgid "Order %1$s" 48 | msgstr "Pedido %1$s" 49 | 50 | msgid "PayPalPlus approval URL not available" 51 | msgstr "URL de aprovação do PayPalPlus não disponível" 52 | 53 | msgid "PayPalPlus requires the country ID of the user" 54 | msgstr "O PayPalPlus exige o ID do país do usuário" 55 | 56 | #, php-format 57 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 58 | msgstr "" 59 | "O pagamento baseado em token falhou com o código \"%1$s\" e a mensagem " 60 | "\"%2$s\": %3$s" 61 | 62 | #, php-format 63 | msgid "Token based payment failed: %1$s" 64 | msgstr "Falha no pagamento baseado em token: %1$s" 65 | 66 | #, php-format 67 | msgid "Token based payment incomplete: %1$s" 68 | msgstr "Pagamento baseado em token incompleto: %1$s" 69 | 70 | #, php-format 71 | msgid "Unexpected redirect: %1$s" 72 | msgstr "Redirecionamento inesperado: %1$s" 73 | -------------------------------------------------------------------------------- /i18n/ru.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Russian (https://app.transifex.com/aimeos/teams/39015/ru/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: ru\n" 22 | "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "Метод \"%1$s\" для провайдера недоступен" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "Для идентификатора клиента \"%1$s\" данные о клиенте Stripe недоступны" 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "Для идентификатора клиента \"%1$s\" недоступен метод оплаты Stripe" 35 | 36 | #, php-format 37 | msgid "No payment token available for customer ID \"%1$s\"" 38 | msgstr "Для идентификатора клиента \"%1$s\" не доступен платежный токен" 39 | 40 | #, php-format 41 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 42 | msgstr "Нет данных о периодических платежах для идентификатора клиента \"%1$s\"" 43 | 44 | #, php-format 45 | msgid "Order %1$s" 46 | msgstr "Заказ %1$s" 47 | 48 | msgid "PayPalPlus approval URL not available" 49 | msgstr "URL-адрес подтверждения PayPalPlus недоступен" 50 | 51 | msgid "PayPalPlus requires the country ID of the user" 52 | msgstr "PayPalPlus требует идентификатор страны пользователя" 53 | 54 | #, php-format 55 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 56 | msgstr "" 57 | "Платеж на основе токена не прошел с кодом \"%1$s\" и сообщением \"%2$s\": " 58 | "%3$s" 59 | 60 | #, php-format 61 | msgid "Token based payment failed: %1$s" 62 | msgstr "Платеж на основе токена не прошел: %1$s" 63 | 64 | #, php-format 65 | msgid "Token based payment incomplete: %1$s" 66 | msgstr "Платеж на основе токена не завершен: %1$s" 67 | 68 | #, php-format 69 | msgid "Unexpected redirect: %1$s" 70 | msgstr "Неожиданное перенаправление: %1$s" 71 | -------------------------------------------------------------------------------- /i18n/ro.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # George Cojocaru, 2024 8 | # Aimeos, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Aimeos, 2025\n" 18 | "Language-Team: Romanian (https://app.transifex.com/aimeos/teams/39015/ro/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: ro\n" 23 | "Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "Metoda \"%1$s\" pentru furnizor nu este disponibilă" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "" 32 | "Nu sunt disponibile date despre clientul Stripe pentru ID-ul clientului " 33 | "\"%1$s\"" 34 | 35 | #, php-format 36 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 37 | msgstr "" 38 | "Nu este disponibilă nicio metodă de plată Stripe pentru ID-ul clientului " 39 | "\"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No payment token available for customer ID \"%1$s\"" 43 | msgstr "" 44 | "Nu există niciun token de plată disponibil pentru ID-ul clientului \"%1$s\"" 45 | 46 | #, php-format 47 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 48 | msgstr "" 49 | "Nu sunt disponibile date de plată recurente pentru ID-ul clientului \"%1$s\"" 50 | 51 | #, php-format 52 | msgid "Order %1$s" 53 | msgstr "Comanda %1$s" 54 | 55 | msgid "PayPalPlus approval URL not available" 56 | msgstr "URL-ul de aprobare PayPalPlus nu este disponibil" 57 | 58 | msgid "PayPalPlus requires the country ID of the user" 59 | msgstr "PayPalPlus solicită ID-ul de țară al utilizatorului" 60 | 61 | #, php-format 62 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 63 | msgstr "Plata bazată pe token a eșuat cu codul \"%1$s\" și mesajul \"%2$s\": %3$s" 64 | 65 | #, php-format 66 | msgid "Token based payment failed: %1$s" 67 | msgstr "Plata pe bază de token a eșuat: %1$s" 68 | 69 | #, php-format 70 | msgid "Token based payment incomplete: %1$s" 71 | msgstr "Plata pe bază de token este incompletă: %1$s" 72 | 73 | #, php-format 74 | msgid "Unexpected redirect: %1$s" 75 | msgstr "Redirecționare neașteptată: %1$s" 76 | -------------------------------------------------------------------------------- /i18n/cs.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Czech (https://app.transifex.com/aimeos/teams/39015/cs/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: cs\n" 22 | "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "Metoda \"%1$s\" pro poskytovatele není k dispozici" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "" 31 | "Pro ID zákazníka \"%1$s\" nejsou k dispozici žádná data zákazníka služby " 32 | "Stripe." 33 | 34 | #, php-format 35 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 36 | msgstr "Pro ID zákazníka \"%1$s\" není k dispozici žádná platební metoda Stripe." 37 | 38 | #, php-format 39 | msgid "No payment token available for customer ID \"%1$s\"" 40 | msgstr "Pro ID zákazníka \"%1$s\" není k dispozici žádný platební token." 41 | 42 | #, php-format 43 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 44 | msgstr "" 45 | "Pro ID zákazníka \"%1$s\" nejsou k dispozici žádné údaje o opakovaných " 46 | "platbách." 47 | 48 | #, php-format 49 | msgid "Order %1$s" 50 | msgstr "Objednávka %1$s" 51 | 52 | msgid "PayPalPlus approval URL not available" 53 | msgstr "Adresa URL pro schválení služby PayPalPlus není k dispozici" 54 | 55 | msgid "PayPalPlus requires the country ID of the user" 56 | msgstr "Služba PayPalPlus vyžaduje ID země uživatele" 57 | 58 | #, php-format 59 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 60 | msgstr "" 61 | "Platba na základě tokenu se nezdařila s kódem \"%1$s\" a zprávou \"%2$s\": " 62 | "%3$s" 63 | 64 | #, php-format 65 | msgid "Token based payment failed: %1$s" 66 | msgstr "Platba na základě tokenu se nezdařila: %1$s" 67 | 68 | #, php-format 69 | msgid "Token based payment incomplete: %1$s" 70 | msgstr "Nedokončená platba na základě tokenu: %1$s" 71 | 72 | #, php-format 73 | msgid "Unexpected redirect: %1$s" 74 | msgstr "Neočekávané přesměrování: %1$s" 75 | -------------------------------------------------------------------------------- /i18n/bg.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Plamen Petkov , 2025 8 | # Aimeos, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Aimeos, 2025\n" 18 | "Language-Team: Bulgarian (https://app.transifex.com/aimeos/teams/39015/bg/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: bg\n" 23 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "Метод \"%1$s\" за доставчик не е възможен" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "" 32 | "Няма налични данни за клиента на Stripe за идентификационния номер на " 33 | "клиента \"%1$s\"" 34 | 35 | #, php-format 36 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 37 | msgstr "" 38 | "Няма наличен метод за плащане на Stripe за идентификационния номер на " 39 | "клиента \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No payment token available for customer ID \"%1$s\"" 43 | msgstr "" 44 | "Няма наличен токен за плащане за идентификационен номер на клиент \"%1$s\"" 45 | 46 | #, php-format 47 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 48 | msgstr "" 49 | "Няма налични данни за периодични плащания за идентификационен номер на " 50 | "клиент \"%1$s\"" 51 | 52 | #, php-format 53 | msgid "Order %1$s" 54 | msgstr "Поръчка %1$s" 55 | 56 | msgid "PayPalPlus approval URL not available" 57 | msgstr "URL адресът за одобрение на PayPalPlus не е наличен" 58 | 59 | msgid "PayPalPlus requires the country ID of the user" 60 | msgstr "PayPalPlus изисква идентификатора на страната на потребителя" 61 | 62 | #, php-format 63 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 64 | msgstr "" 65 | "Плащането на базата на токени е неуспешно с код \"%1$s\" и съобщение " 66 | "\"%2$s\": %3$s" 67 | 68 | #, php-format 69 | msgid "Token based payment failed: %1$s" 70 | msgstr "Неуспешно плащане на базата на токени: %1$s" 71 | 72 | #, php-format 73 | msgid "Token based payment incomplete: %1$s" 74 | msgstr "Непълно плащане на базата на токени: %1$s" 75 | 76 | #, php-format 77 | msgid "Unexpected redirect: %1$s" 78 | msgstr "Неочаквано пренасочване: %1$s " 79 | -------------------------------------------------------------------------------- /tests/MShop/Service/Provider/Payment/AuthorizeDPMTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped( 'Omnipay library not available' ); 22 | } 23 | 24 | $this->context = \TestHelper::context(); 25 | 26 | $conf = array( 27 | 'address' => '1', 28 | 'onsite' => '1', 29 | 'testmode' => true, 30 | ); 31 | 32 | $serviceManager = \Aimeos\MShop::create( $this->context, 'service' ); 33 | $item = $serviceManager->create(); 34 | $item->setCode( 'omnipaytest' ); 35 | $item->setConfig( $conf ); 36 | 37 | $this->object = $this->getMockBuilder( 'Aimeos\MShop\Service\Provider\Payment\AuthorizeDPM' ) 38 | ->onlyMethods( array( 'save', 'getProvider' ) ) 39 | ->setConstructorArgs( array( $this->context, $item ) ) 40 | ->getMock(); 41 | } 42 | 43 | 44 | protected function tearDown() : void 45 | { 46 | unset( $this->object, $this->context ); 47 | } 48 | 49 | 50 | public function testGetValueType() 51 | { 52 | $this->assertEquals( 'AuthorizeNet_DPM', $this->access( 'getValue' )->invokeArgs( $this->object, ['type'] ) ); 53 | } 54 | 55 | 56 | public function testGetValueOnsite() 57 | { 58 | $this->assertTrue( $this->access( 'getValue' )->invokeArgs( $this->object, ['onsite'] ) ); 59 | } 60 | 61 | 62 | public function testGetValueTestmode() 63 | { 64 | $this->assertTrue( $this->access( 'getValue' )->invokeArgs( $this->object, ['testmode'] ) ); 65 | } 66 | 67 | 68 | public function testProcessOnsiteAddress() 69 | { 70 | $result = $this->object->process( $this->getOrder(), [] ); 71 | 72 | $this->assertInstanceOf( \Aimeos\MShop\Common\Helper\Form\Iface::class, $result ); 73 | } 74 | 75 | 76 | protected function access( $name ) 77 | { 78 | $class = new \ReflectionClass( \Aimeos\MShop\Service\Provider\Payment\AuthorizeDPM::class ); 79 | $method = $class->getMethod( $name ); 80 | $method->setAccessible( true ); 81 | 82 | return $method; 83 | } 84 | 85 | 86 | protected function getOrder() 87 | { 88 | $manager = \Aimeos\MShop::create( $this->context, 'order' ); 89 | $search = $manager->filter()->add( 'order.datepayment', '==', '2008-02-15 12:34:56' ); 90 | 91 | return $manager->search( $search, ['order', 'order/address', 'order/service'] ) 92 | ->first( new \RuntimeException( 'No order found' ) ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /i18n/pl.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Aimeos, 2025 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 15 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 16 | "Last-Translator: Aimeos, 2025\n" 17 | "Language-Team: Polish (https://app.transifex.com/aimeos/teams/39015/pl/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: pl\n" 22 | "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" 23 | 24 | #, php-format 25 | msgid "Method \"%1$s\" for provider not available" 26 | msgstr "Metoda \"%1$s\" dla dostawcy niedostępna" 27 | 28 | #, php-format 29 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 30 | msgstr "Brak dostępnych danych klienta Stripe dla identyfikatora klienta \"%1$s\"" 31 | 32 | #, php-format 33 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 34 | msgstr "" 35 | "Brak dostępnej metody płatności Stripe dla identyfikatora klienta \"%1$s\"" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Brak dostępnego tokena płatności dla identyfikatora klienta \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "" 44 | "Brak dostępnych danych płatności cyklicznych dla identyfikatora klienta " 45 | "\"%1$s\"" 46 | 47 | #, php-format 48 | msgid "Order %1$s" 49 | msgstr "Zamówienie %1$s" 50 | 51 | msgid "PayPalPlus approval URL not available" 52 | msgstr "Adres URL zatwierdzenia PayPalPlus nie jest dostępny" 53 | 54 | msgid "PayPalPlus requires the country ID of the user" 55 | msgstr "PayPalPlus wymaga podania identyfikatora kraju użytkownika" 56 | 57 | #, php-format 58 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 59 | msgstr "" 60 | "Płatność oparta na tokenie nie powiodła się z kodem \"%1$s\" i komunikatem " 61 | "\"%2$s\": %3$s" 62 | 63 | #, php-format 64 | msgid "Token based payment failed: %1$s" 65 | msgstr "Płatność oparta na tokenie nie powiodła się: %1$s" 66 | 67 | #, php-format 68 | msgid "Token based payment incomplete: %1$s" 69 | msgstr "Płatność oparta na tokenach niekompletna: %1$s" 70 | 71 | #, php-format 72 | msgid "Unexpected redirect: %1$s" 73 | msgstr "Nieoczekiwane przekierowanie: %1$s" 74 | -------------------------------------------------------------------------------- /i18n/uk.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Федір Вільгота , 2025 8 | # Aimeos, 2025 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2025-10-16 11:59+0200\n" 16 | "PO-Revision-Date: 2022-02-27 11:04+0000\n" 17 | "Last-Translator: Aimeos, 2025\n" 18 | "Language-Team: Ukrainian (https://app.transifex.com/aimeos/teams/39015/uk/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: uk\n" 23 | "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" 24 | 25 | #, php-format 26 | msgid "Method \"%1$s\" for provider not available" 27 | msgstr "Метод \"%1$s\" для провайдера недоступний" 28 | 29 | #, php-format 30 | msgid "No Stripe customer data available for customer ID \"%1$s\"" 31 | msgstr "Для ідентифікатора клієнта \"%1$s\" немає даних про клієнта Stripe" 32 | 33 | #, php-format 34 | msgid "No Stripe payment method available for customer ID \"%1$s\"" 35 | msgstr "Для ідентифікатора клієнта \"%1$s\" метод оплати Stripe недоступний" 36 | 37 | #, php-format 38 | msgid "No payment token available for customer ID \"%1$s\"" 39 | msgstr "Жоден платіжний токен не доступний для ID клієнта \"%1$s\"" 40 | 41 | #, php-format 42 | msgid "No reoccurring payment data available for customer ID \"%1$s\"" 43 | msgstr "Немає даних про платежі, що повторюються, для ID клієнта \"%1$s\"" 44 | 45 | #, php-format 46 | msgid "Order %1$s" 47 | msgstr "Замовлення %1$s" 48 | 49 | msgid "PayPalPlus approval URL not available" 50 | msgstr "URL-адреса схвалення PayPalPlus недоступна" 51 | 52 | msgid "PayPalPlus requires the country ID of the user" 53 | msgstr "PayPalPlus вимагає ідентифікатор країни користувача" 54 | 55 | #, php-format 56 | msgid "Token based payment failed with code \"%1$s\" and message \"%2$s\": %3$s" 57 | msgstr "" 58 | "Платіж на основі токенів не відбувся з кодом \"%1$s\" та повідомленням " 59 | "\"%2$s\": %3$s" 60 | 61 | #, php-format 62 | msgid "Token based payment failed: %1$s" 63 | msgstr "Помилка оплати на основі токену: %1$s" 64 | 65 | #, php-format 66 | msgid "Token based payment incomplete: %1$s" 67 | msgstr "Платіж на основі токенів незавершений: %1$s" 68 | 69 | #, php-format 70 | msgid "Unexpected redirect: %1$s" 71 | msgstr "Несподіване переспрямування: %1$s" 72 | -------------------------------------------------------------------------------- /src/MShop/Service/Provider/Payment/Mpay24.php: -------------------------------------------------------------------------------- 1 | data( $order->getCustomerId(), 'repay' ) ) === null ) 34 | { 35 | $msg = sprintf( 'No reoccurring payment data available for customer ID "%1$s"', $order->getCustomerId() ); 36 | throw new \Aimeos\MShop\Service\Exception( $msg ); 37 | } 38 | 39 | if( !isset( $cfg['token'] ) ) 40 | { 41 | $msg = sprintf( 'No payment token available for customer ID "%1$s"', $order->getCustomerId() ); 42 | throw new \Aimeos\MShop\Service\Exception( $msg ); 43 | } 44 | 45 | $data = array( 46 | 'transactionId' => $order->getId(), 47 | 'currency' => $order->getPrice()->getCurrencyId(), 48 | 'amount' => $this->getAmount( $order->getPrice() ), 49 | 'cardReference' => $cfg['token'], 50 | 'paymentPage' => false, 51 | 'language' => 'en', 52 | ); 53 | 54 | if( $this->getValue( 'address', false ) ) { 55 | $data['card'] = $this->getCardDetails( $order, [] ); 56 | } 57 | 58 | $provider = \Omnipay\Omnipay::create( 'Mpay24_Backend' ); 59 | $provider->setTestMode( (bool) $this->getValue( 'testmode', false ) ); 60 | $provider->initialize( $this->getServiceItem()->getConfig() ); 61 | $response = $provider->purchase( $data )->send(); 62 | 63 | if( $response->isSuccessful() || $response->isPending() ) 64 | { 65 | $this->setOrderData( $order, ['TRANSACTIONID' => $response->getTransactionReference()] ); 66 | $order->setStatusPayment( \Aimeos\MShop\Order\Item\Base::PAY_RECEIVED ); 67 | } 68 | elseif( !$response->getTransactionReference() ) 69 | { 70 | $msg = 'Token based payment incomplete: ' . print_r( $response->getData(), true ); 71 | throw new \Aimeos\MShop\Service\Exception( $msg, 1 ); 72 | } 73 | else 74 | { 75 | $msg = sprintf( 'Token based payment failed with code "%1$s" and message "%2$s": %3$s', 76 | $response->getCode(), $response->getMessage(), print_r( $response->getData(), true ) ); 77 | throw new \Aimeos\MShop\Service\Exception( $msg, -1 ); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/MShop/Service/Provider/Payment/Payone.php: -------------------------------------------------------------------------------- 1 | getProducts() as $product ) 37 | { 38 | $list = $product->toArray(); 39 | 40 | $lines[] = new \Omnipay\Payone\Extend\Item([ 41 | 'id' => $list['order.product.prodcode'], 42 | 'name' => $product->getName(), 43 | 'itemType' => 'goods', // Available types: goods, shipping etc. 44 | 'quantity' => $product->getQuantity(), 45 | 'price' => $product->getPrice()->getValue(), 46 | 'vat' => (int) $product->getPrice()->getTaxRate(), 47 | ]); 48 | } 49 | 50 | foreach( $order->getService( 'delivery' ) as $delivery ) 51 | { 52 | if( $delivery->getPrice()->getCosts() != '0.00' ) 53 | { 54 | $lines[] = new \Omnipay\Payone\Extend\Item([ 55 | 'id' => $delivery->getId(), 56 | 'name' => $delivery->getName(), 57 | 'itemType' => 'shipment', 58 | 'quantity' => 1, 59 | 'price' => $delivery->getPrice()->getCosts(), 60 | 'vat' => (int) $delivery->getPrice()->getTaxRate(), 61 | ]); 62 | } 63 | } 64 | 65 | $data = parent::getData( $order, $orderid, $params ); 66 | $data['items'] = new \Omnipay\Common\ItemBag( $lines ); 67 | $data['accessMethod'] = 'classic'; 68 | 69 | return $data; 70 | } 71 | 72 | 73 | /** 74 | * Updates the order status sent by payment gateway notifications 75 | * 76 | * @param \Psr\Http\Message\ServerRequestInterface Request object 77 | * @return \Psr\Http\Message\ResponseInterface Response object 78 | */ 79 | public function updatePush( \Psr\Http\Message\ServerRequestInterface $request, 80 | \Psr\Http\Message\ResponseInterface $response ) : \Psr\Http\Message\ResponseInterface 81 | { 82 | $params = (array) $request->getAttributes() + (array) $request->getParsedBody() + (array) $request->getQueryParams(); 83 | 84 | if( isset( $params['reference'] ) ) 85 | { 86 | $response = parent::updatePush( $request->withAttribute( 'orderid', $params['reference'] ), $response ); 87 | 88 | if( $response->getStatusCode() === 200 ) { 89 | $response = $response->withBody( $response->createStreamFromString( 'TSOK' ) ); // payment update successful 90 | } 91 | } 92 | 93 | return $response; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tests/MShop/Service/Provider/Payment/PayoneTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped( 'Omnipay library not available' ); 17 | } 18 | 19 | $this->context = \TestHelper::context(); 20 | 21 | $serviceManager = \Aimeos\MShop::create( $this->context, 'service' ); 22 | $this->serviceItem = $serviceManager->create(); 23 | $this->serviceItem->setConfig( array( 'type' => 'Dummy' ) ); 24 | $this->serviceItem->setCode( 'unitpaymentcode' ); 25 | 26 | $this->object = $this->getMockBuilder( \Aimeos\MShop\Service\Provider\Payment\Payone::class ) 27 | ->setConstructorArgs( array( $this->context, $this->serviceItem ) ) 28 | ->onlyMethods( ['getProvider', 'save', 'updateSync', 'saveRepayData'] ) 29 | ->getMock(); 30 | } 31 | 32 | 33 | protected function tearDown() : void 34 | { 35 | unset( $this->object ); 36 | unset( $this->context ); 37 | unset( $this->serviceItem ); 38 | } 39 | 40 | 41 | public function testProcessOffsitePurchaseSuccess() 42 | { 43 | $provider = new \Omnipay\Dummy\Gateway(); 44 | 45 | $this->object->expects( $this->once() )->method( 'getProvider' ) 46 | ->willReturn( $provider ); 47 | 48 | $this->serviceItem->setConfig( array( 'type' => 'Dummy', 'address' => '1' ) ); 49 | 50 | $params = array( 51 | 'number' => '4929000000006', 52 | 'expiryMonth' => '1', 53 | 'expiryYear' => '2099', 54 | ); 55 | $result = $this->object->process( $this->getOrder(), $params ); 56 | 57 | $this->assertInstanceOf( \Aimeos\MShop\Common\Helper\Form\Iface::class, $result ); 58 | } 59 | 60 | 61 | public function testUpdatePush() 62 | { 63 | $psr7stream = $this->getMockBuilder( \Psr\Http\Message\StreamInterface::class )->getMock(); 64 | $psr7request = $this->getMockBuilder( \Psr\Http\Message\ServerRequestInterface::class )->getMock(); 65 | $psr7response = $this->getMockBuilder( \Aimeos\Base\View\Helper\Response\Iface::class )->getMock(); 66 | 67 | $psr7request->expects( $this->exactly( 2 ) )->method( 'getAttributes' ) 68 | ->willReturn( ['reference' => 1] ); 69 | 70 | $psr7request->expects( $this->once() )->method( 'withAttribute' ) 71 | ->willReturn( $psr7request ); 72 | 73 | $psr7response->expects( $this->once() )->method( 'getStatusCode' ) 74 | ->willReturn( 200 ); 75 | 76 | $psr7response->expects( $this->once() )->method( 'withBody' ) 77 | ->willReturn( $psr7response ); 78 | 79 | $psr7response->expects( $this->once() )->method( 'createStreamFromString' ) 80 | ->willReturn( $psr7stream ); 81 | 82 | $result = $this->object->updatePush( $psr7request, $psr7response ); 83 | 84 | $this->assertInstanceOf( \Psr\Http\Message\ResponseInterface::class, $result ); 85 | } 86 | 87 | 88 | protected function getOrder() 89 | { 90 | $manager = \Aimeos\MShop::create( $this->context, 'order' ); 91 | $search = $manager->filter()->add( 'order.datepayment', '==', '2008-02-15 12:34:56' ); 92 | 93 | return $manager->search( $search, ['order', 'order/address', 'order/product', 'order/service'] ) 94 | ->first( new \RuntimeException( 'No order found' ) ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/MShop/Service/Provider/Payment/AuthorizeSIM.php: -------------------------------------------------------------------------------- 1 | array( 26 | 'code' => 'payment.url-success', 27 | 'internalcode'=> 'payment.url-success', 28 | 'label'=> 'URL to confirm page', 29 | 'type'=> 'string', 30 | 'internaltype'=> 'string', 31 | 'default'=> '', 32 | 'required'=> true, 33 | ), 34 | ); 35 | 36 | 37 | /** 38 | * Returns the configuration attribute definitions of the provider to generate a list of available fields and 39 | * rules for the value of each field in the administration interface. 40 | * 41 | * @return array List of attribute definitions implementing \Aimeos\Base\Critera\Attribute\Iface 42 | */ 43 | public function getConfigBE() : array 44 | { 45 | $list = parent::getConfigBE(); 46 | 47 | foreach( $this->beConfig as $key => $config ) 48 | { 49 | $config['code'] = $config['code']; 50 | $list[$key] = new \Aimeos\Base\Criteria\Attribute\Standard( $config ); 51 | } 52 | 53 | return $list; 54 | } 55 | 56 | 57 | /** 58 | * Checks the backend configuration attributes for validity. 59 | * 60 | * @param array $attributes Attributes added by the shop owner in the administraton interface 61 | * @return array An array with the attribute keys as key and an error message as values for all attributes that are 62 | * known by the provider but aren't valid 63 | */ 64 | public function checkConfigBE( array $attributes ) : array 65 | { 66 | return array_merge( parent::checkConfigBE( $attributes ), $this->checkConfig( $this->beConfig, $attributes ) ); 67 | } 68 | 69 | 70 | /** 71 | * Updates the order status sent by payment gateway notifications 72 | * 73 | * @param \Psr\Http\Message\ServerRequestInterface Request object 74 | * @return \Psr\Http\Message\ResponseInterface Response object 75 | */ 76 | public function updatePush( \Psr\Http\Message\ServerRequestInterface $request, 77 | \Psr\Http\Message\ResponseInterface $response ) : \Psr\Http\Message\ResponseInterface 78 | { 79 | $params = (array) $request->getAttributes() + (array) $request->getParsedBody() + (array) $request->getQueryParams(); 80 | 81 | if( isset( $params['x_MD5_Hash'] ) ) 82 | { 83 | $url = $this->getConfigValue( array( 'payment.url-success' ) ); 84 | $output = sprintf( $this->getValue( 'body', 'success' ), $url ); 85 | 86 | $response = parent::updatePush( $request, $response ); 87 | $response = $response->withBody( $response->createStreamFromString( $output ) ); 88 | $response = $response->withHeader( 'Location', $url ); 89 | } 90 | 91 | return $response; 92 | } 93 | 94 | 95 | /** 96 | * Returns the value for the given configuration key 97 | * 98 | * @param string $key Configuration key name 99 | * @param mixed $default Default value if no configuration is found 100 | * @return mixed Configuration value 101 | */ 102 | protected function getValue( string $key, $default = null ) 103 | { 104 | switch( $key ) { 105 | case 'type': return 'AuthorizeNet_SIM'; 106 | } 107 | 108 | return parent::getValue( $key, $default ); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /phing.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Aimeos logo 3 | 4 | 5 | # Aimeos payment extension 6 | 7 | [![Build Status](https://circleci.com/gh/aimeoscom/ai-payments.svg?style=shield)](https://circleci.com/gh/aimeoscom/ai-payments) 8 | [![Coverage Status](https://codecov.io/gh/aimeoscom/ai-payments/branch/master/graph/badge.svg?token=ilbfKmIhfs)](https://codecov.io/gh/aimeoscom/ai-payments) 9 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/aimeoscom/ai-payments/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/aimeos/ai-payments/?branch=master) 10 | [![License](https://poser.pugx.org/aimeos/ai-payments/license.svg)](https://packagist.org/packages/aimeoscom/ai-payments) 11 | 12 | Aimeos extension for additional payment methods and their service provider. 13 | Some of them may have beta quality and improvements or contributions are always welcome! 14 | 15 | **Tip:** There's also [commercial support](https://aimeos.com/support/) available if you need help to get a specific provider working. 16 | 17 | ## Table of contents 18 | 19 | - [Installation](#installation) 20 | - [Configuration](#configuration) 21 | - [License](#license) 22 | - [Links](#links) 23 | 24 | ## Installation 25 | 26 | As every Aimeos extension, the easiest way is to install it via [composer](https://getcomposer.org/). If you don't have composer installed yet, you can execute this string on the command line to download it: 27 | ``` 28 | php -r "readfile('https://getcomposer.org/installer');" | php -- --filename=composer 29 | ``` 30 | 31 | Add the cache extension name to the "require" section of your ```composer.json``` file: 32 | ``` 33 | "require": [ 34 | "aimeos/ai-payments": "dev-master", 35 | ... 36 | ], 37 | ``` 38 | You should use a stable release if you don't want to add code or improve the implementation. The available stable versions can be found on [Packagist](https://packagist.org/packages/aimeos/ai-payments). 39 | 40 | Afterwards you only need to execute the composer update command on the command line: 41 | ``` 42 | composer update 43 | ``` 44 | 45 | These commands will install the Aimeos extension into the extension directory and it will be available immediately. 46 | 47 | ## Configuration 48 | 49 | Payment options are configured via the shop administration interface in the ["Service" tab](https://aimeos.org/docs/User_Manual/Administration_Interface/Service_list) and you can add as many payment options as you need to the list for each site. They will be shown on the payment page in the checkout process. In the detail view of a new payment option, you have to enter some values: 50 | 51 | ![Aimeos payment detail view](https://aimeos.org/docs/images/Admin-backend-service-detail-payment2.png) 52 | 53 | Make sure you set the status to "enabled" and the type to "Payment". Use an unique code for the payment option, idealy it should be readable and consist only of characters a-z, 0-9 and a few special characters like "-", "_" or ".". The value for the field "Provider" must be the last part of the class name of the payment service provider. Each of the following sections will tell you how it must be named. The last input field influences the position of the payment option within the list of payment options and you should use zero for the top position and greater values for the next payment options. 54 | 55 | In the right side of the panel you can add the configuration settings that are specifically required for each payment provider. The list of available settings for each payment provider can be found in the [service documentation](https://aimeos.org/docs/User_Manual/Administration_Interface/Service_list#Supported_by_ai-payments). 56 | 57 | ## License 58 | 59 | The Aimeos payments extension is licensed under the terms of the LGPLv3 Open Source license and is available for free. 60 | 61 | ## Links 62 | 63 | * [Web site](https://aimeos.org/) 64 | * [Documentation](https://aimeos.org/docs) 65 | * [Help](https://aimeos.org/help) 66 | * [Issue tracker](https://github.com/aimeoscom/ai-payments/issues) 67 | * [Source code](https://github.com/aimeoscom/ai-payments) 68 | -------------------------------------------------------------------------------- /tests/MShop/Service/Provider/Payment/AuthorizeSIMTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped( 'Omnipay library not available' ); 23 | } 24 | 25 | $this->context = \TestHelper::context(); 26 | 27 | $serviceManager = \Aimeos\MShop::create( $this->context, 'service' ); 28 | $this->serviceItem = $serviceManager->create(); 29 | $this->serviceItem->setConfig( array( 'authorizenet.testmode' => true ) ); 30 | $this->serviceItem->setCode( 'unitpaymentcode' ); 31 | 32 | $this->object = $this->getMockBuilder( 'Aimeos\MShop\Service\Provider\Payment\AuthorizeSIM' ) 33 | ->onlyMethods( array( 'save', 'getProvider' ) ) 34 | ->setConstructorArgs( array( $this->context, $this->serviceItem ) ) 35 | ->getMock(); 36 | } 37 | 38 | 39 | protected function tearDown() : void 40 | { 41 | unset( $this->object, $this->context, $this->serviceItem ); 42 | } 43 | 44 | 45 | public function testGetConfigBE() 46 | { 47 | $object = new \Aimeos\MShop\Service\Provider\Payment\AuthorizeSIM( $this->context, $this->serviceItem ); 48 | 49 | $result = $object->getConfigBE(); 50 | 51 | $this->assertIsArray( $result ); 52 | $this->assertArrayHasKey( 'address', $result ); 53 | $this->assertArrayHasKey( 'authorize', $result ); 54 | $this->assertArrayHasKey( 'testmode', $result ); 55 | $this->assertArrayHasKey( 'createtoken', $result ); 56 | $this->assertArrayHasKey( 'onsite', $result ); 57 | $this->assertArrayHasKey( 'type', $result ); 58 | $this->assertArrayHasKey( 'payment.url-success', $result ); 59 | } 60 | 61 | 62 | public function testCheckConfigBE() 63 | { 64 | $object = new \Aimeos\MShop\Service\Provider\Payment\AuthorizeSIM( $this->context, $this->serviceItem ); 65 | 66 | $attributes = array( 'payment.url-success' => 'https://localhost', 'type' => 'AuthorizeNet_SIM' ); 67 | 68 | $result = $object->checkConfigBE( $attributes ); 69 | 70 | $this->assertEquals( 7, count( $result ) ); 71 | $this->assertEquals( null, $result['address'] ); 72 | $this->assertEquals( null, $result['authorize'] ); 73 | $this->assertEquals( null, $result['testmode'] ); 74 | $this->assertEquals( null, $result['createtoken'] ); 75 | $this->assertEquals( null, $result['onsite'] ); 76 | $this->assertEquals( null, $result['type'] ); 77 | $this->assertEquals( null, $result['payment.url-success'] ); 78 | } 79 | 80 | 81 | public function testGetValueType() 82 | { 83 | $this->assertEquals( 'AuthorizeNet_SIM', $this->access( 'getValue' )->invokeArgs( $this->object, ['type'] ) ); 84 | } 85 | 86 | 87 | public function testUpdatePush() 88 | { 89 | $psr7stream = $this->getMockBuilder( \Psr\Http\Message\StreamInterface::class )->getMock(); 90 | $psr7request = $this->getMockBuilder( \Psr\Http\Message\ServerRequestInterface::class )->getMock(); 91 | $psr7response = $this->getMockBuilder( \Aimeos\Base\View\Helper\Response\Iface::class )->getMock(); 92 | 93 | $psr7request->expects( $this->exactly( 2 ) )->method( 'getAttributes' ) 94 | ->willReturn( ['x_MD5_Hash' => 1] ); 95 | 96 | $psr7response->expects( $this->once() )->method( 'withBody' ) 97 | ->willReturn( $psr7response ); 98 | 99 | $psr7response->expects( $this->once() )->method( 'withHeader' ) 100 | ->willReturn( $psr7response ); 101 | 102 | $psr7response->expects( $this->once() )->method( 'createStreamFromString' ) 103 | ->willReturn( $psr7stream ); 104 | 105 | $result = $this->object->updatePush( $psr7request, $psr7response ); 106 | 107 | $this->assertInstanceOf( \Psr\Http\Message\ResponseInterface::class, $result ); 108 | } 109 | 110 | 111 | protected function access( $name ) 112 | { 113 | $class = new \ReflectionClass( \Aimeos\MShop\Service\Provider\Payment\AuthorizeSIM::class ); 114 | $method = $class->getMethod( $name ); 115 | $method->setAccessible( true ); 116 | 117 | return $method; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tests/MShop/Service/Provider/Payment/StripeTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped( 'Omnipay library not available' ); 22 | } 23 | 24 | $this->context = \TestHelper::context(); 25 | $config = ['type' => 'Stripe_PaymentIntents', 'testmode' => true]; 26 | $item = \Aimeos\MShop::create( $this->context, 'service' )->create()->setConfig( $config )->setCode( 'unitpaymentcode' ); 27 | 28 | $this->object = new Stripe( $this->context, $item ); 29 | } 30 | 31 | 32 | protected function tearDown() : void 33 | { 34 | unset( $this->object, $this->context ); 35 | } 36 | 37 | 38 | public function testGetConfigBE() 39 | { 40 | $result = $this->object->getConfigBE(); 41 | 42 | $this->assertIsArray( $result ); 43 | $this->assertArrayHasKey( 'apiKey', $result ); 44 | $this->assertArrayHasKey( 'publishableKey', $result ); 45 | $this->assertArrayHasKey( 'address', $result ); 46 | $this->assertArrayHasKey( 'authorize', $result ); 47 | $this->assertArrayHasKey( 'testmode', $result ); 48 | $this->assertArrayHasKey( 'createtoken', $result ); 49 | $this->assertArrayHasKey( 'onsite', $result ); 50 | $this->assertArrayHasKey( 'type', $result ); 51 | } 52 | 53 | 54 | public function testCheckConfigBE() 55 | { 56 | $attributes = array( 57 | 'apiKey' => '123', 58 | 'publishableKey' => 'abc', 59 | 'address' => '0', 60 | 'authorize' => '1', 61 | 'testmode' => '1', 62 | 'type' => 'Stripe', 63 | ); 64 | 65 | $result = $this->object->checkConfigBE( $attributes ); 66 | 67 | $this->assertEquals( 8, count( $result ) ); 68 | $this->assertEquals( null, $result['apiKey'] ); 69 | $this->assertEquals( null, $result['publishableKey'] ); 70 | $this->assertEquals( null, $result['address'] ); 71 | $this->assertEquals( null, $result['authorize'] ); 72 | $this->assertEquals( null, $result['createtoken'] ); 73 | $this->assertEquals( null, $result['testmode'] ); 74 | $this->assertEquals( null, $result['onsite'] ); 75 | } 76 | 77 | 78 | public function testGetData() 79 | { 80 | $order = $this->getOrder(); 81 | $result = $this->access( 'getData' )->invokeArgs( $this->object, [$order, $order->getId(), []] ); 82 | 83 | $this->assertArrayNotHasKey( 'token', $result ); 84 | } 85 | 86 | 87 | public function testGetDataToken() 88 | { 89 | $order = $this->getOrder(); 90 | $result = $this->access( 'getData' )->invokeArgs( $this->object, [$order, $order->getId(), ['paymenttoken' => 'abc']] ); 91 | 92 | $this->assertArrayHasKey( 'token', $result ); 93 | } 94 | 95 | 96 | public function testGetProvider() 97 | { 98 | $result = $this->access( 'getProvider' )->invokeArgs( $this->object, [] ); 99 | $this->assertInstanceOf( \Omnipay\Common\GatewayInterface::class, $result ); 100 | } 101 | 102 | 103 | public function testCheckConfigFE() 104 | { 105 | $this->assertEquals( [], $this->object->checkConfigFE( [] ) ); 106 | } 107 | 108 | 109 | public function testGetConfigFE() 110 | { 111 | $basket = \Aimeos\MShop::create( $this->context, 'order' )->create(); 112 | $this->assertEquals( [], $this->object->getConfigFE( $basket ) ); 113 | } 114 | 115 | 116 | public function testProcess() 117 | { 118 | $iface = \Aimeos\MShop\Common\Helper\Form\Iface::class; 119 | $order = \Aimeos\MShop::create( $this->context, 'order' )->create(); 120 | 121 | $this->assertInstanceOf( $iface, $this->object->process( $order ) ); 122 | } 123 | 124 | 125 | protected function getOrder() 126 | { 127 | $manager = \Aimeos\MShop::create( $this->context, 'order' ); 128 | $search = $manager->filter()->add( 'order.datepayment', '==', '2008-02-15 12:34:56' ); 129 | 130 | return $manager->search( $search, ['order', 'order/product', 'order/service'] ) 131 | ->first( new \RuntimeException( 'No order found' ) ); 132 | } 133 | 134 | 135 | protected function access( $name ) 136 | { 137 | $class = new \ReflectionClass( \Aimeos\MShop\Service\Provider\Payment\Stripe::class ); 138 | $method = $class->getMethod( $name ); 139 | $method->setAccessible( true ); 140 | 141 | return $method; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /tests/MShop/Service/Provider/Payment/NovalnetSepaTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped( 'Omnipay library not available' ); 24 | } 25 | 26 | $this->context = \TestHelper::context(); 27 | 28 | $serviceManager = \Aimeos\MShop::create( $this->context, 'service' ); 29 | $this->serviceItem = $serviceManager->create(); 30 | $this->serviceItem->setConfig( array( 'type' => 'Dummy' ) ); 31 | $this->serviceItem->setCode( 'unitpaymentcode' ); 32 | 33 | $this->ordServItem = \Aimeos\MShop::create( $this->context, 'order/service' )->create(); 34 | $serviceItem = \Aimeos\MShop::create( $this->context, 'service' )->create(); 35 | $serviceItem->setCode( 'unitpaymentcode' ); 36 | 37 | $this->object = $this->getMockBuilder( '\\Aimeos\\MShop\\Service\\Provider\\Payment\\NovalnetSepa' ) 38 | ->onlyMethods( ['save', 'getProvider', 'saveRepayData'] ) 39 | ->setConstructorArgs( array( $this->context, $serviceItem ) ) 40 | ->getMock(); 41 | } 42 | 43 | 44 | protected function tearDown() : void 45 | { 46 | unset( $this->object ); 47 | } 48 | 49 | 50 | public function testGetConfigFE() 51 | { 52 | $status = \Aimeos\MShop\Order\Item\Base::PAY_AUTHORIZED; 53 | $orderManager = \Aimeos\MShop::create( $this->context, 'order' ); 54 | $search = $orderManager->filter()->add( [ 55 | 'order.channel' => 'web', 56 | 'order.statuspayment' => $status 57 | ] ); 58 | 59 | $item = $orderManager->search( $search, ['order', 'order/address'] ) 60 | ->first( new \RuntimeException( sprintf( 'No order found with status "%1$s" and channel "%2$s"', $status, 'web' ) ) ); 61 | 62 | $config = $this->object->getConfigFE( $item ); 63 | 64 | $this->assertArrayHasKey( 'novalnetsepa.iban', $config ); 65 | } 66 | 67 | 68 | public function testCheckConfigFE() 69 | { 70 | $config = array( 71 | 'novalnetsepa.bic' => 'ABCDEFGHIJK', 72 | 'novalnetsepa.iban' => 'DE00102030405060708090', 73 | 'novalnetsepa.holder' => 'test user', 74 | ); 75 | 76 | $result = $this->object->checkConfigFE( $config ); 77 | 78 | $expected = array( 79 | 'novalnetsepa.bic' => null, 80 | 'novalnetsepa.iban' => null, 81 | 'novalnetsepa.holder' => null, 82 | ); 83 | 84 | $this->assertEquals( $expected, $result ); 85 | } 86 | 87 | 88 | public function testSetConfigFE() 89 | { 90 | $params = array( 91 | 'novalnetsepa.bic' => 'ABCDEFGHIJK', 92 | 'novalnetsepa.iban' => 'DE00102030405060708090', 93 | 'novalnetsepa.holder' => 'test user', 94 | ); 95 | 96 | $this->object->setConfigFE( $this->ordServItem, $params ); 97 | 98 | $attrItem = $this->ordServItem->getAttributeItem( 'novalnetsepa.iban', 'session' ); 99 | $this->assertInstanceOf( '\\Aimeos\\MShop\\Order\\Item\\Service\\Attribute\\Iface', $attrItem ); 100 | $this->assertEquals( 'DE00102030405060708090', $attrItem->getValue() ); 101 | } 102 | 103 | 104 | public function testProcess() 105 | { 106 | $provider = $this->getMockBuilder( 'Omnipay\Dummy\Gateway' ) 107 | ->onlyMethods( array( 'purchase' ) ) 108 | ->getMock(); 109 | 110 | $request = $this->getMockBuilder( \Omnipay\Common\Message\AbstractRequest::class ) 111 | ->disableOriginalConstructor() 112 | ->getMock(); 113 | 114 | $response = $this->getMockBuilder( 'Omnipay\Dummy\Message\Response' ) 115 | ->onlyMethods( array( 'getTransactionReference', 'isSuccessful' ) ) 116 | ->disableOriginalConstructor() 117 | ->getMock(); 118 | 119 | $this->object->expects( $this->once() )->method( 'getProvider' ) 120 | ->willReturn( $provider ); 121 | 122 | $provider->expects( $this->once() )->method( 'purchase' ) 123 | ->willReturn( $request ); 124 | 125 | $request->expects( $this->once() )->method( 'send' ) 126 | ->willReturn( $response ); 127 | 128 | $response->expects( $this->once() )->method( 'isSuccessful' ) 129 | ->willReturn( true ); 130 | 131 | $response->expects( $this->once() )->method( 'getTransactionReference' ) 132 | ->willReturn( '' ); 133 | 134 | 135 | $result = $this->object->process( $this->getOrder() ); 136 | 137 | $this->assertInstanceOf( \Aimeos\MShop\Common\Helper\Form\Iface::class, $result ); 138 | } 139 | 140 | 141 | protected function getOrder() 142 | { 143 | $manager = \Aimeos\MShop::create( $this->context, 'order' ); 144 | $search = $manager->filter()->add( 'order.datepayment', '==', '2008-02-15 12:34:56' ); 145 | 146 | return $manager->search( $search, ['order', 'order/product', 'order/service'] ) 147 | ->first( new \RuntimeException( 'No order found' ) ); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /tests/MShop/Service/Provider/Payment/NovalnetCreditTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped( 'Omnipay library not available' ); 24 | } 25 | 26 | $this->context = \TestHelper::context(); 27 | 28 | $serviceManager = \Aimeos\MShop::create( $this->context, 'service' ); 29 | $this->serviceItem = $serviceManager->create(); 30 | $this->serviceItem->setConfig( array( 'type' => 'Dummy', 'address' => 1 ) ); 31 | $this->serviceItem->setCode( 'unitpaymentcode' ); 32 | 33 | $this->ordServItem = \Aimeos\MShop::create( $this->context, 'order/service' )->create(); 34 | $serviceItem = \Aimeos\MShop::create( $this->context, 'service' )->create(); 35 | $serviceItem->setCode( 'unitpaymentcode' ); 36 | 37 | $this->object = $this->getMockBuilder( '\\Aimeos\\MShop\\Service\\Provider\\Payment\\NovalnetCredit' ) 38 | ->onlyMethods( ['save', 'getProvider', 'getValue', 'saveRepayData'] ) 39 | ->setConstructorArgs( array( $this->context, $serviceItem ) ) 40 | ->getMock(); 41 | } 42 | 43 | 44 | protected function tearDown() : void 45 | { 46 | unset( $this->object ); 47 | } 48 | 49 | 50 | public function testGetConfigFE() 51 | { 52 | $status = \Aimeos\MShop\Order\Item\Base::PAY_AUTHORIZED; 53 | $manager = \Aimeos\MShop::create( $this->context, 'order' ); 54 | $search = $manager->filter()->add( [ 55 | 'order.channel' => 'web', 56 | 'order.statuspayment' => $status 57 | ] ); 58 | 59 | $item = $manager->search( $search, ['order', 'order/address'] ) 60 | ->first( new \RuntimeException( sprintf( 'No order found with status "%1$s" and channel "%2$s"', $status, 'web' ) ) ); 61 | 62 | $config = $this->object->getConfigFE( $item ); 63 | 64 | $this->assertEquals( 'Our Unittest', $config['novalnetcredit.holder']->getDefault() ); 65 | $this->assertArrayHasKey( 'novalnetcredit.number', $config ); 66 | $this->assertArrayHasKey( 'novalnetcredit.year', $config ); 67 | $this->assertArrayHasKey( 'novalnetcredit.month', $config ); 68 | $this->assertArrayHasKey( 'novalnetcredit.cvv', $config ); 69 | } 70 | 71 | 72 | public function testCheckConfigFE() 73 | { 74 | $config = array( 75 | 'novalnetcredit.holder' => 'test user', 76 | 'novalnetcredit.number' => '4111111111111111', 77 | 'novalnetcredit.year' => date( 'Y' ), 78 | 'novalnetcredit.month' => '1', 79 | 'novalnetcredit.cvv' => '123', 80 | ); 81 | 82 | $result = $this->object->checkConfigFE( $config ); 83 | 84 | $expected = array( 85 | 'novalnetcredit.holder' => null, 86 | 'novalnetcredit.number' => null, 87 | 'novalnetcredit.year' => null, 88 | 'novalnetcredit.month' => null, 89 | 'novalnetcredit.cvv' => null, 90 | ); 91 | 92 | $this->assertEquals( $expected, $result ); 93 | } 94 | 95 | 96 | public function testSetConfigFE() 97 | { 98 | $this->object->setConfigFE( $this->ordServItem, array( 'novalnetcredit.number' => '4111111111111111' ) ); 99 | 100 | $attrItem = $this->ordServItem->getAttributeItem( 'novalnetcredit.number', 'session' ); 101 | $this->assertInstanceOf( '\\Aimeos\\MShop\\Order\\Item\\Service\\Attribute\\Iface', $attrItem ); 102 | $this->assertEquals( '4111111111111111', $attrItem->getValue() ); 103 | } 104 | 105 | 106 | public function testProcess() 107 | { 108 | $provider = $this->getMockBuilder( 'Omnipay\Dummy\Gateway' ) 109 | ->onlyMethods( array( 'purchase' ) ) 110 | ->getMock(); 111 | 112 | $request = $this->getMockBuilder( \Omnipay\Common\Message\AbstractRequest::class ) 113 | ->disableOriginalConstructor() 114 | ->getMock(); 115 | 116 | $response = $this->getMockBuilder( 'Omnipay\Dummy\Message\Response' ) 117 | ->onlyMethods( array( 'getTransactionReference', 'isSuccessful' ) ) 118 | ->disableOriginalConstructor() 119 | ->getMock(); 120 | 121 | $this->object->expects( $this->once() )->method( 'getProvider' ) 122 | ->willReturn( $provider ); 123 | 124 | $provider->expects( $this->once() )->method( 'purchase' ) 125 | ->willReturn( $request ); 126 | 127 | $request->expects( $this->once() )->method( 'send' ) 128 | ->willReturn( $response ); 129 | 130 | $response->expects( $this->once() )->method( 'isSuccessful' ) 131 | ->willReturn( true ); 132 | 133 | $response->expects( $this->once() )->method( 'getTransactionReference' ) 134 | ->willReturn( '' ); 135 | 136 | 137 | $result = $this->object->process( $this->getOrder() ); 138 | 139 | $this->assertInstanceOf( \Aimeos\MShop\Common\Helper\Form\Iface::class, $result ); 140 | } 141 | 142 | 143 | public function testGetCardDetails() 144 | { 145 | $this->object->expects( $this->once() )->method( 'getValue' ) 146 | ->willReturn( true ); 147 | 148 | $result = $this->access( 'getCardDetails' )->invokeArgs( $this->object, [$this->getOrder(), []] ); 149 | $this->assertInstanceOf( \Omnipay\Common\CreditCard::class, $result ); 150 | } 151 | 152 | 153 | protected function getOrder() 154 | { 155 | $manager = \Aimeos\MShop::create( $this->context, 'order' ); 156 | $search = $manager->filter()->add( ['order.datepayment' => '2008-02-15 12:34:56'] ); 157 | 158 | return $manager->search( $search, ['order', 'order/product', 'order/service'] ) 159 | ->first( new \RuntimeException( 'No order found' ) ); 160 | } 161 | 162 | 163 | protected function access( $name ) 164 | { 165 | $class = new \ReflectionClass( \Aimeos\MShop\Service\Provider\Payment\NovalnetCredit::class ); 166 | $method = $class->getMethod( $name ); 167 | $method->setAccessible( true ); 168 | 169 | return $method; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/MShop/Service/Provider/Payment/AuthorizeDPM.php: -------------------------------------------------------------------------------- 1 | array( 26 | 'code' => 'payment.firstname', 27 | 'internalcode'=> 'x_first_name', 28 | 'label'=> 'First name', 29 | 'type'=> 'string', 30 | 'internaltype'=> 'string', 31 | 'default'=> '', 32 | 'required'=> false 33 | ), 34 | 'payment.lastname' => array( 35 | 'code' => 'payment.lastname', 36 | 'internalcode'=> 'x_last_name', 37 | 'label'=> 'Last name', 38 | 'type'=> 'string', 39 | 'internaltype'=> 'string', 40 | 'default'=> '', 41 | 'required'=> true 42 | ), 43 | 'payment.cardno' => array( 44 | 'code' => 'payment.cardno', 45 | 'internalcode'=> 'x_card_num', 46 | 'label'=> 'Credit card number', 47 | 'type'=> 'number', 48 | 'internaltype'=> 'integer', 49 | 'default'=> '', 50 | 'required'=> true 51 | ), 52 | 'payment.cvv' => array( 53 | 'code' => 'payment.cvv', 54 | 'internalcode'=> 'x_card_code', 55 | 'label'=> 'Verification number', 56 | 'type'=> 'number', 57 | 'internaltype'=> 'integer', 58 | 'default'=> '', 59 | 'required'=> true 60 | ), 61 | 'payment.expirymonthyear' => array( 62 | 'code' => 'payment.expirymonthyear', 63 | 'internalcode'=> 'x_exp_date', 64 | 'label'=> 'Expiry date', 65 | 'type'=> 'number', 66 | 'internaltype'=> 'integer', 67 | 'default'=> '', 68 | 'required'=> true 69 | ), 70 | 'payment.company' => array( 71 | 'code' => 'payment.company', 72 | 'internalcode'=> 'x_company', 73 | 'label'=> 'Company', 74 | 'type'=> 'string', 75 | 'internaltype'=> 'string', 76 | 'default'=> '', 77 | 'required'=> false, 78 | 'public' => false, 79 | ), 80 | 'payment.address1' => array( 81 | 'code' => 'payment.address1', 82 | 'internalcode'=> 'x_address', 83 | 'label'=> 'Street', 84 | 'type'=> 'string', 85 | 'internaltype'=> 'string', 86 | 'default'=> '', 87 | 'required'=> false, 88 | 'public' => false, 89 | ), 90 | 'payment.city' => array( 91 | 'code' => 'payment.city', 92 | 'internalcode'=> 'x_city', 93 | 'label'=> 'City', 94 | 'type'=> 'string', 95 | 'internaltype'=> 'string', 96 | 'default'=> '', 97 | 'required'=> false, 98 | 'public' => false, 99 | ), 100 | 'payment.postal' => array( 101 | 'code' => 'payment.postal', 102 | 'internalcode'=> 'x_zip', 103 | 'label'=> 'Zip code', 104 | 'type'=> 'string', 105 | 'internaltype'=> 'string', 106 | 'default'=> '', 107 | 'required'=> false, 108 | 'public' => false, 109 | ), 110 | 'payment.countryid' => array( 111 | 'code' => 'payment.countryid', 112 | 'internalcode'=> 'x_country', 113 | 'label'=> 'Country', 114 | 'type'=> 'string', 115 | 'internaltype'=> 'string', 116 | 'default'=> '', 117 | 'required'=> false, 118 | 'public' => false, 119 | ), 120 | 'payment.telephone' => array( 121 | 'code' => 'payment.telephone', 122 | 'internalcode'=> 'x_phone', 123 | 'label'=> 'Telephone', 124 | 'type'=> 'string', 125 | 'internaltype'=> 'string', 126 | 'default'=> '', 127 | 'required'=> false, 128 | 'public' => false, 129 | ), 130 | 'payment.email' => array( 131 | 'code' => 'payment.email', 132 | 'internalcode'=> 'x_email', 133 | 'label'=> 'E-Mail', 134 | 'type'=> 'string', 135 | 'internaltype'=> 'string', 136 | 'default'=> '', 137 | 'required'=> false, 138 | 'public' => false, 139 | ), 140 | ); 141 | 142 | 143 | /** 144 | * Returns the payment form for entering payment details at the shop site. 145 | * 146 | * @param \Aimeos\MShop\Order\Item\Iface $order Order object 147 | * @param array $params Request parameter if available 148 | * @return \Aimeos\MShop\Common\Helper\Form\Iface Form helper object 149 | */ 150 | protected function getPaymentForm( \Aimeos\MShop\Order\Item\Iface $order, array $params ) : \Aimeos\MShop\Common\Helper\Form\Iface 151 | { 152 | $list = []; 153 | $feConfig = $this->feConfig; 154 | $addresses = $order->getAddress( \Aimeos\MShop\Order\Item\Address\Base::TYPE_PAYMENT ); 155 | 156 | if( ( $address = current( $addresses ) ) !== false ) 157 | { 158 | if( !isset( $params[$feConfig['payment.firstname']['internalcode']] ) 159 | || $params[$feConfig['payment.firstname']['internalcode']] == '' 160 | ) { 161 | $feConfig['payment.firstname']['default'] = $address->getFirstname(); 162 | } 163 | 164 | if( !isset( $params[$feConfig['payment.lastname']['internalcode']] ) 165 | || $params[$feConfig['payment.lastname']['internalcode']] == '' 166 | ) { 167 | $feConfig['payment.lastname']['default'] = $address->getLastname(); 168 | } 169 | 170 | if( $this->getValue( 'address' ) ) 171 | { 172 | $feConfig['payment.address1']['default'] = $address->getAddress1() . ' ' . $address->getAddress2(); 173 | $feConfig['payment.city']['default'] = $address->getCity(); 174 | $feConfig['payment.postal']['default'] = $address->getPostal(); 175 | $feConfig['payment.countryid']['default'] = $address->getCountryId(); 176 | $feConfig['payment.telephone']['default'] = $address->getTelephone(); 177 | $feConfig['payment.company']['default'] = $address->getCompany(); 178 | $feConfig['payment.email']['default'] = $address->getEmail(); 179 | } 180 | } 181 | 182 | foreach( $feConfig as $key => $config ) { 183 | $list[$key] = new \Aimeos\Base\Criteria\Attribute\Standard( $config ); 184 | } 185 | 186 | $url = $this->getConfigValue( 'payment.url-self', '' ); 187 | return new \Aimeos\MShop\Common\Helper\Form\Standard( $url, 'POST', $list, false ); 188 | } 189 | 190 | 191 | /** 192 | * Returns the value for the given configuration key 193 | * 194 | * @param string $key Configuration key name 195 | * @param mixed $default Default value if no configuration is found 196 | * @return mixed Configuration value 197 | */ 198 | protected function getValue( string $key, $default = null ) 199 | { 200 | switch( $key ) 201 | { 202 | case 'type': return 'AuthorizeNet_DPM'; 203 | case 'onsite': return true; 204 | } 205 | 206 | return parent::getValue( $key, $default ); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/MShop/Service/Provider/Payment/NovalnetSepa.php: -------------------------------------------------------------------------------- 1 | array( 26 | 'code' => 'novalnetsepa.holder', 27 | 'internalcode'=> 'holder', 28 | 'label'=> 'Bank account holder', 29 | 'type'=> 'string', 30 | 'internaltype'=> 'string', 31 | 'default'=> '', 32 | 'required'=> true 33 | ), 34 | 'novalnetsepa.iban' => array( 35 | 'code' => 'novalnetsepa.iban', 36 | 'internalcode'=> 'iban', 37 | 'label'=> 'IBAN number', 38 | 'type'=> 'string', 39 | 'internaltype'=> 'string', 40 | 'default'=> '', 41 | 'required'=> true 42 | ), 43 | 'novalnetsepa.bic' => array( 44 | 'code' => 'novalnetsepa.bic', 45 | 'internalcode'=> 'bic', 46 | 'label'=> 'BIC code', 47 | 'type'=> 'string', 48 | 'internaltype'=> 'string', 49 | 'default'=> '', 50 | 'required'=> true 51 | ), 52 | ); 53 | 54 | 55 | /** 56 | * Returns the configuration attribute definitions of the provider to generate a list of available fields and 57 | * rules for the value of each field in the frontend. 58 | * 59 | * @param \Aimeos\MShop\Order\Item\Iface $basket Basket object 60 | * @return array List of attribute definitions implementing \Aimeos\Base\Critera\Attribute\Iface 61 | */ 62 | public function getConfigFE( \Aimeos\MShop\Order\Item\Iface $basket ) : array 63 | { 64 | $list = []; 65 | $feconfig = $this->feConfig; 66 | 67 | try 68 | { 69 | $service = $basket->getService( \Aimeos\MShop\Order\Item\Service\Base::TYPE_PAYMENT, 0 ); 70 | 71 | foreach( $service->getAttributeItems() as $item ) 72 | { 73 | if( isset( $feconfig[$item->getCode()] ) ) { 74 | $feconfig[$item->getCode()]['default'] = $item->getValue(); 75 | } 76 | } 77 | } 78 | catch( \Aimeos\MShop\Order\Exception $e ) {; } // If payment isn't available yet 79 | 80 | 81 | $addresses = $basket->getAddress( \Aimeos\MShop\Order\Item\Address\Base::TYPE_PAYMENT ); 82 | 83 | if( ( $address = current( $addresses ) ) !== false ) 84 | { 85 | if( $feconfig['novalnetsepa.holder']['default'] == '' 86 | && ( $fn = $address->getFirstname() ) !== '' && ( $ln = $address->getLastname() ) !== '' 87 | ) { 88 | $feconfig['novalnetsepa.holder']['default'] = $fn . ' ' . $ln; 89 | } 90 | } 91 | 92 | 93 | foreach( $feconfig as $key => $config ) { 94 | $list[$key] = new \Aimeos\Base\Criteria\Attribute\Standard( $config ); 95 | } 96 | 97 | return $list; 98 | } 99 | 100 | 101 | /** 102 | * Checks the frontend configuration attributes for validity. 103 | * 104 | * @param array $attributes Attributes entered by the customer during the checkout process 105 | * @return array An array with the attribute keys as key and an error message as values for all attributes that are 106 | * known by the provider but aren't valid resp. null for attributes whose values are OK 107 | */ 108 | public function checkConfigFE( array $attributes ) : array 109 | { 110 | return $this->checkConfig( $this->feConfig, $attributes ); 111 | } 112 | 113 | 114 | /** 115 | * Sets the payment attributes in the given service. 116 | * 117 | * @param \Aimeos\MShop\Order\Item\Service\Iface $orderServiceItem Order service item that will be added to the basket 118 | * @param array $attributes Attribute key/value pairs entered by the customer during the checkout process 119 | * @return \Aimeos\MShop\Order\Item\Service\Iface Order service item with attributes added 120 | */ 121 | public function setConfigFE( \Aimeos\MShop\Order\Item\Service\Iface $orderServiceItem, 122 | array $attributes ) : \Aimeos\MShop\Order\Item\Service\Iface 123 | { 124 | return $orderServiceItem->addAttributeItems( $this->attributes( $attributes, 'session' ) ); 125 | } 126 | 127 | 128 | /** 129 | * Tries to get an authorization or captures the money immediately for the given order if capturing the money 130 | * separately isn't supported or not configured by the shop owner. 131 | * 132 | * @param \Aimeos\MShop\Order\Item\Iface $order Order invoice object 133 | * @param array $params Request parameter if available 134 | * @return \Aimeos\MShop\Common\Helper\Form\Iface|null Form object with URL, action and parameters to redirect to 135 | * (e.g. to an external server of the payment provider or to a local success page) 136 | */ 137 | public function process( \Aimeos\MShop\Order\Item\Iface $order, array $params = [] ) : ?\Aimeos\MShop\Common\Helper\Form\Iface 138 | { 139 | return $this->processOrder( $order, $params ); 140 | } 141 | 142 | 143 | /** 144 | * Returns the data passed to the Omnipay library 145 | * 146 | * @param \Aimeos\MShop\Order\Item\Iface $order Basket object 147 | * @param string $orderid Unique order ID 148 | * @param array $params Request parameter if available 149 | * @return array Associative list of key/value pairs 150 | */ 151 | protected function getData( \Aimeos\MShop\Order\Item\Iface $order, string $orderid, array $params ) : array 152 | { 153 | $urls = $this->getPaymentUrls(); 154 | $card = $this->getCardDetails( $order, $params ); 155 | $desc = $this->context()->translate( 'mshop', 'Order %1$s' ); 156 | $addresses = $order->getAddress( \Aimeos\MShop\Order\Item\Address\Base::TYPE_PAYMENT ); 157 | 158 | if( ( $address = current( $addresses ) ) !== false ) { 159 | $langid = $address->getLanguageId(); 160 | } else { 161 | $langid = $this->context()->locale()->getLanguageId(); 162 | } 163 | 164 | $data = array( 165 | 'token' => '', 166 | 'card' => $card, 167 | 'language' => $langid, 168 | 'transactionId' => $orderid, 169 | 'description' => sprintf( $desc, $orderid ), 170 | 'amount' => $this->getAmount( $order->getPrice() ), 171 | 'currency' => $order->locale()->getCurrencyId(), 172 | 'clientIp' => $this->getValue( 'client.ipaddress' ), 173 | 'bic' => ( isset( $params['novalnetsepa.bic'] ) ? $params['novalnetsepa.bic'] : '' ), 174 | 'iban' => ( isset( $params['novalnetsepa.iban'] ) ? $params['novalnetsepa.iban'] : '' ), 175 | 'bankaccount_holder' => ( isset( $params['novalnetsepa.holder'] ) ? $params['novalnetsepa.holder'] : '' ), 176 | ) + $urls; 177 | 178 | return $data; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/MShop/Service/Provider/Payment/Datatrans.php: -------------------------------------------------------------------------------- 1 | array( 29 | 'code' => 'type', 30 | 'internalcode'=> 'type', 31 | 'label'=> 'Payment provider type', 32 | 'type'=> 'string', 33 | 'internaltype'=> 'string', 34 | 'default'=> 'Datatrans', 35 | 'required'=> true, 36 | ), 37 | 'merchantId' => array( 38 | 'code' => 'merchantId', 39 | 'internalcode'=> 'merchantId', 40 | 'label'=> 'Merchant ID from the Datatrans account', 41 | 'type'=> 'string', 42 | 'internaltype'=> 'string', 43 | 'default'=> '', 44 | 'required'=> true, 45 | ), 46 | 'sign' => array( 47 | 'code' => 'sign', 48 | 'internalcode'=> 'sign', 49 | 'label'=> 'Sign identifier available in the datatrans backend', 50 | 'type'=> 'string', 51 | 'internaltype'=> 'string', 52 | 'default'=> '', 53 | 'required'=> true, 54 | ), 55 | 'hmacKey1' => array( 56 | 'code' => 'hmacKey1', 57 | 'internalcode'=> 'hmacKey1', 58 | 'label'=> 'SHA256 pre-shared key for signing requests', 59 | 'type'=> 'string', 60 | 'internaltype'=> 'string', 61 | 'default'=> '', 62 | 'required'=> false, 63 | ), 64 | 'password' => array( 65 | 'code' => 'password', 66 | 'internalcode'=> 'password', 67 | 'label'=> 'Password for server to server connections', 68 | 'type'=> 'string', 69 | 'internaltype'=> 'string', 70 | 'default'=> '', 71 | 'required'=> false, 72 | ), 73 | ); 74 | 75 | 76 | /** 77 | * Checks the backend configuration attributes for validity. 78 | * 79 | * @param array $attributes Attributes added by the shop owner in the administraton interface 80 | * @return array An array with the attribute keys as key and an error message as values for all attributes that are 81 | * known by the provider but aren't valid resp. null for attributes whose values are OK 82 | */ 83 | public function checkConfigBE( array $attributes ) : array 84 | { 85 | return array_merge( parent::checkConfigBE( $attributes ), $this->checkConfig( $this->beConfig, $attributes ) ); 86 | } 87 | 88 | 89 | /** 90 | * Returns the configuration attribute definitions of the provider to generate a list of available fields and 91 | * rules for the value of each field in the administration interface. 92 | * 93 | * @return array List of attribute definitions implementing \Aimeos\Base\Critera\Attribute\Iface 94 | */ 95 | public function getConfigBE() : array 96 | { 97 | $list = parent::getConfigBE(); 98 | 99 | foreach( $this->beConfig as $key => $config ) { 100 | $list[$key] = new \Aimeos\Base\Criteria\Attribute\Standard( $config ); 101 | } 102 | 103 | return $list; 104 | } 105 | 106 | 107 | /** 108 | * Queries for status updates for the given order compare with the responseCode 109 | * 110 | * @param \Aimeos\MShop\Order\Item\Iface $order Order item 111 | * @return \Aimeos\MShop\Order\Item\Iface Updated order item 112 | */ 113 | public function query( \Aimeos\MShop\Order\Item\Iface $order ) : \Aimeos\MShop\Order\Item\Iface 114 | { 115 | $response = $this->getProvider()->getTransaction( ['transactionId' => $order->getId()] )->send(); 116 | 117 | if( $response->isSuccessful() ) 118 | { 119 | if( in_array( $response->getResponseCode(), [2, 3, 21] ) ) { 120 | $order->setStatusPayment( \Aimeos\MShop\Order\Item\Base::PAY_RECEIVED ); 121 | } elseif( $response->getResponseCode() == 1 ) { 122 | $order->setStatusPayment( \Aimeos\MShop\Order\Item\Base::PAY_AUTHORIZED ); 123 | } 124 | } 125 | elseif( method_exists($response, 'isPending') && $response->isPending() ) 126 | { 127 | $order->setStatusPayment( \Aimeos\MShop\Order\Item\Base::PAY_PENDING ); 128 | } 129 | elseif( method_exists($response, 'isCancelled') && $response->isCancelled() ) 130 | { 131 | $order->setStatusPayment( \Aimeos\MShop\Order\Item\Base::PAY_CANCELED ); 132 | } 133 | 134 | $this->setOrderData( $order, ['TRANSACTIONID' => $response->getTransactionReference()] ); 135 | return $order; 136 | } 137 | 138 | 139 | /** 140 | * Executes the payment again for the given order if supported. 141 | * This requires support of the payment gateway and token based payment 142 | * 143 | * @param \Aimeos\MShop\Order\Item\Iface $order Order invoice object 144 | * @return \Aimeos\MShop\Order\Item\Iface Updated order item 145 | */ 146 | public function repay( \Aimeos\MShop\Order\Item\Iface $order ) : \Aimeos\MShop\Order\Item\Iface 147 | { 148 | if( ( $cfg = $this->data( $order->getCustomerId(), 'repay' ) ) === null ) 149 | { 150 | $msg = sprintf( 'No reoccurring payment data available for customer ID "%1$s"', $order->getCustomerId() ); 151 | throw new \Aimeos\MShop\Service\Exception( $msg ); 152 | } 153 | 154 | if( !isset( $cfg['token'] ) ) 155 | { 156 | $msg = sprintf( 'No payment token available for customer ID "%1$s"', $order->getCustomerId() ); 157 | throw new \Aimeos\MShop\Service\Exception( $msg ); 158 | } 159 | 160 | $data = array( 161 | 'transactionId' => $order->getId(), 162 | 'currency' => $order->getPrice()->getCurrencyId(), 163 | 'amount' => $this->getAmount( $order->getPrice() ), 164 | 'cardReference' => $cfg['token'], 165 | 'paymentPage' => false, 166 | ); 167 | 168 | if( isset( $cfg['month'] ) && isset( $cfg['year'] ) ) 169 | { 170 | $data['card'] = new \Omnipay\Common\CreditCard( [ 171 | 'expiryMonth' => $cfg['month'], 172 | 'expiryYear' => $cfg['year'], 173 | ] ); 174 | } 175 | 176 | $response = $this->getXmlProvider()->purchase( $data )->send(); 177 | 178 | if( $response->isSuccessful() || $response->isPending() ) 179 | { 180 | $this->setOrderData( $order, ['TRANSACTIONID' => $response->getTransactionReference()] ); 181 | $order->setStatusPayment( \Aimeos\MShop\Order\Item\Base::PAY_RECEIVED ); 182 | } 183 | elseif( !$response->getTransactionReference() ) 184 | { 185 | $msg = $this->context()->translate( 'mshop', 'Token based payment incomplete: %1$s' ); 186 | throw new \Aimeos\MShop\Service\Exception( sprintf( $msg, print_r( $response->getData(), true ) ), 1 ); 187 | } 188 | else 189 | { 190 | $str = ( method_exists( $response, 'getMessage' ) ? $response->getMessage() : '' ); 191 | $msg = $this->context()->translate( 'mshop', 'Token based payment failed: %1$s' ); 192 | throw new \Aimeos\MShop\Service\Exception( sprintf( $msg, $str ), -1 ); 193 | } 194 | 195 | return $order; 196 | } 197 | 198 | 199 | /** 200 | * Returns the value for the given configuration key 201 | * 202 | * @param string $key Configuration key name 203 | * @param mixed $default Default value if no configuration is found 204 | * @return mixed Configuration value 205 | */ 206 | protected function getValue( string $key, $default = null ) 207 | { 208 | switch( $key ) { 209 | case 'type': return 'Datatrans'; 210 | } 211 | 212 | return parent::getValue( $key, $default ); 213 | } 214 | 215 | 216 | /** 217 | * Returns the Datatrans XML payment provider 218 | * 219 | * @return \Omnipay\Common\GatewayInterface Gateway provider object 220 | */ 221 | protected function getXmlProvider() : \Omnipay\Common\GatewayInterface 222 | { 223 | $provider = OPay::create('Datatrans\Xml'); 224 | $provider->initialize( $this->getServiceItem()->getConfig() ); 225 | 226 | return $provider; 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | -------------------------------------------------------------------------------- /src/MShop/Service/Provider/Payment/NovalnetCredit.php: -------------------------------------------------------------------------------- 1 | array( 26 | 'code' => 'novalnetcredit.holder', 27 | 'internalcode'=> 'ccholder', 28 | 'label'=> 'Credit card owner', 29 | 'type'=> 'string', 30 | 'internaltype'=> 'string', 31 | 'default'=> '', 32 | 'required'=> true 33 | ), 34 | 'novalnetcredit.number' => array( 35 | 'code' => 'novalnetcredit.number', 36 | 'internalcode'=> 'ccnumber', 37 | 'label'=> 'Credit card number', 38 | 'type'=> 'string', 39 | 'internaltype'=> 'string', 40 | 'default'=> '', 41 | 'required'=> true 42 | ), 43 | 'novalnetcredit.year' => array( 44 | 'code' => 'novalnetcredit.year', 45 | 'internalcode'=> 'ccyear', 46 | 'label'=> 'Expiry year', 47 | 'type'=> 'select', 48 | 'internaltype'=> 'integer', 49 | 'default'=> '', 50 | 'required'=> true 51 | ), 52 | 'novalnetcredit.month' => array( 53 | 'code' => 'novalnetcredit.month', 54 | 'internalcode'=> 'ccmonth', 55 | 'label'=> 'Expiry month', 56 | 'type'=> 'select', 57 | 'internaltype'=> 'integer', 58 | 'default'=> array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ), 59 | 'required'=> true 60 | ), 61 | 'novalnetcredit.cvv' => array( 62 | 'code' => 'novalnetcredit.cvv', 63 | 'internalcode'=> 'cccvv', 64 | 'label'=> 'CVC code', 65 | 'type'=> 'string', 66 | 'internaltype'=> 'string', 67 | 'default'=> '', 68 | 'required'=> true 69 | ), 70 | ); 71 | 72 | 73 | /** 74 | * Initializes the service provider object. 75 | * 76 | * @param \Aimeos\MShop\ContextIface $context Context object with required objects 77 | * @param \Aimeos\MShop\Service\Item\Iface $serviceItem Service item with configuration for the provider 78 | */ 79 | public function __construct( \Aimeos\MShop\ContextIface $context, \Aimeos\MShop\Service\Item\Iface $serviceItem ) 80 | { 81 | parent::__construct( $context, $serviceItem ); 82 | 83 | $year = date( 'Y' ); 84 | $this->feConfig['novalnetcredit.year']['default'] = array( $year, $year + 1, $year + 2, $year + 3, $year + 4, $year + 5, $year + 6, $year + 7 ); 85 | } 86 | 87 | 88 | /** 89 | * Returns the configuration attribute definitions of the provider to generate a list of available fields and 90 | * rules for the value of each field in the frontend. 91 | * 92 | * @param \Aimeos\MShop\Order\Item\Iface $basket Basket object 93 | * @return array List of attribute definitions implementing \Aimeos\Base\Critera\Attribute\Iface 94 | */ 95 | public function getConfigFE( \Aimeos\MShop\Order\Item\Iface $basket ) : array 96 | { 97 | $list = []; 98 | $feconfig = $this->feConfig; 99 | 100 | try 101 | { 102 | $service = $basket->getService( \Aimeos\MShop\Order\Item\Service\Base::TYPE_PAYMENT, 0 ); 103 | 104 | foreach( $service->getAttributeItems() as $item ) 105 | { 106 | if( isset( $feconfig[$item->getCode()] ) ) 107 | { 108 | if( is_array( $feconfig[$item->getCode()]['default'] ) ) { 109 | $feconfig[$item->getCode()]['default'] = array_merge( array( $item->getValue() ), $feconfig[$item->getCode()]['default'] ); 110 | } else { 111 | $feconfig[$item->getCode()]['default'] = $item->getValue(); 112 | } 113 | } 114 | } 115 | } 116 | catch( \Aimeos\MShop\Order\Exception $e ) {; } // If payment isn't available yet 117 | 118 | 119 | $addresses = $basket->getAddress( \Aimeos\MShop\Order\Item\Address\Base::TYPE_PAYMENT ); 120 | 121 | if( ( $address = current( $addresses ) ) !== false ) 122 | { 123 | if( $feconfig['novalnetcredit.holder']['default'] == '' 124 | && ( $fn = $address->getFirstname() ) !== '' && ( $ln = $address->getLastname() ) !== '' 125 | ) { 126 | $feconfig['novalnetcredit.holder']['default'] = $fn . ' ' . $ln; 127 | } 128 | } 129 | 130 | 131 | foreach( $feconfig as $key => $config ) { 132 | $list[$key] = new \Aimeos\Base\Criteria\Attribute\Standard( $config ); 133 | } 134 | 135 | return $list; 136 | } 137 | 138 | 139 | /** 140 | * Checks the frontend configuration attributes for validity. 141 | * 142 | * @param array $attributes Attributes entered by the customer during the checkout process 143 | * @return array An array with the attribute keys as key and an error message as values for all attributes that are 144 | * known by the provider but aren't valid resp. null for attributes whose values are OK 145 | */ 146 | public function checkConfigFE( array $attributes ) : array 147 | { 148 | return $this->checkConfig( $this->feConfig, $attributes ); 149 | } 150 | 151 | 152 | /** 153 | * Sets the payment attributes in the given service. 154 | * 155 | * @param \Aimeos\MShop\Order\Item\Service\Iface $orderServiceItem Order service item that will be added to the basket 156 | * @param array $attributes Attribute key/value pairs entered by the customer during the checkout process 157 | * @return \Aimeos\MShop\Order\Item\Service\Iface Order service item with attributes added 158 | */ 159 | public function setConfigFE( \Aimeos\MShop\Order\Item\Service\Iface $orderServiceItem, 160 | array $attributes ) : \Aimeos\MShop\Order\Item\Service\Iface 161 | { 162 | return $orderServiceItem->addAttributeItems( $this->attributes( $attributes, 'session' ) ); 163 | } 164 | 165 | 166 | /** 167 | * Tries to get an authorization or captures the money immediately for the given order if capturing the money 168 | * separately isn't supported or not configured by the shop owner. 169 | * 170 | * @param \Aimeos\MShop\Order\Item\Iface $order Order invoice object 171 | * @param array $params Request parameter if available 172 | * @return \Aimeos\MShop\Common\Helper\Form\Iface|null Form object with URL, action and parameters to redirect to 173 | * (e.g. to an external server of the payment provider or to a local success page) 174 | */ 175 | public function process( \Aimeos\MShop\Order\Item\Iface $order, array $params = [] ) : ?\Aimeos\MShop\Common\Helper\Form\Iface 176 | { 177 | return $this->processOrder( $order, $params ); 178 | } 179 | 180 | 181 | /** 182 | * Returns an Omnipay credit card object 183 | * 184 | * @param \Aimeos\MShop\Order\Item\Iface $order Order object with addresses and services 185 | * @param array $params POST parameters passed to the provider 186 | * @return \Omnipay\Common\CreditCard Credit card object 187 | */ 188 | protected function getCardDetails( \Aimeos\MShop\Order\Item\Iface $order, array $params ) : \Omnipay\Common\CreditCard 189 | { 190 | $addresses = $order->getAddress( \Aimeos\MShop\Order\Item\Address\Base::TYPE_PAYMENT ); 191 | 192 | if( $this->getValue( 'address' ) && ( $addr = current( $addresses ) ) !== false ) 193 | { 194 | $params['billingName'] = $addr->getFirstname() . ' ' . $addr->getLastname(); 195 | $params['billingFirstName'] = $addr->getFirstname(); 196 | $params['billingLastName'] = $addr->getLastname(); 197 | $params['billingCompany'] = $addr->getCompany(); 198 | $params['billingAddress1'] = $addr->getAddress1(); 199 | $params['billingAddress2'] = $addr->getAddress2(); 200 | $params['billingCity'] = $addr->getCity(); 201 | $params['billingPostcode'] = $addr->getPostal(); 202 | $params['billingState'] = $addr->getState(); 203 | $params['billingCountry'] = $addr->getCountryId(); 204 | $params['billingPhone'] = $addr->getTelephone(); 205 | $params['billingFax'] = $addr->getTelefax(); 206 | $params['email'] = $addr->getEmail(); 207 | } 208 | 209 | $params['holder'] = ( isset( $params['novalnetcredit.holder'] ) ? $params['novalnetcredit.holder'] : '' ); 210 | $params['number'] = ( isset( $params['novalnetcredit.number'] ) ? $params['novalnetcredit.number'] : '' ); 211 | $params['expiryYear'] = ( isset( $params['novalnetcredit.year'] ) ? $params['novalnetcredit.year'] : '' ); 212 | $params['expiryMonth'] = ( isset( $params['novalnetcredit.month'] ) ? $params['novalnetcredit.month'] : '' ); 213 | $params['cvv'] = ( isset( $params['novalnetcredit.cvv'] ) ? $params['novalnetcredit.cvv'] : '' ); 214 | 215 | return new \Omnipay\Common\CreditCard( $params ); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # PHP CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-php/ for more details 4 | # 5 | version: 2.1 6 | 7 | jobs: 8 | "php81-mysql": 9 | docker: 10 | - image: aimeos/ci-php:8.1 11 | - image: mysql:latest 12 | environment: 13 | MYSQL_ROOT_PASSWORD: rootpw 14 | MYSQL_DATABASE: aimeos 15 | MYSQL_USER: aimeos 16 | MYSQL_PASSWORD: aimeos 17 | steps: 18 | - checkout 19 | - run: git clone --depth=50 --branch=master https://github.com/aimeos/aimeos-core ../aimeos-core 20 | - run: cd .. && mv project aimeos-core/ext/ai-payments && mv aimeos-core project && cd project 21 | - restore_cache: 22 | keys: 23 | - php81-{{ checksum "composer.json" }} 24 | - run: composer config --no-plugins allow-plugins true 25 | - run: composer req --no-update "php-http/guzzle6-adapter" "omnipay/common:~3.0" "academe/omnipay-datatrans:~3.0" "omnipay/dummy:~3.0" "academe/omnipay-payone:~3.0" "omnipay/stripe:~3.0" 26 | - run: composer update -n --prefer-dist 27 | - save_cache: 28 | key: php81-{{ checksum "composer.json" }} 29 | paths: [./vendor] 30 | - run: echo " array( 'adapter' => 'mysql', 'host' => '127.0.0.1', 'port' => 3306, 'database' => 'aimeos', 'username' => 'aimeos', 'password' => 'aimeos', 'limit' => 2, 'opt-persistent' => false, 'stmt' => array( \"SET SESSIOn sort_buffer_size=2097144; SET NAMES 'utf8'; SET SESSION sql_mode='ANSI'\" ) ), 'fs' => array( 'adapter' => 'Standard', 'basedir' => '.' ), 'mq' => array( 'adapter' => 'Standard', 'db' => 'db' ) );" > config/resource.php 31 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done 32 | - run: ./vendor/bin/phing -Ddir=ext/ai-payments setup testext 33 | 34 | "php82-mysql": 35 | docker: 36 | - image: aimeos/ci-php:8.2 37 | - image: mysql:latest 38 | environment: 39 | MYSQL_ROOT_PASSWORD: rootpw 40 | MYSQL_DATABASE: aimeos 41 | MYSQL_USER: aimeos 42 | MYSQL_PASSWORD: aimeos 43 | steps: 44 | - checkout 45 | - run: git clone --depth=50 --branch=master https://github.com/aimeos/aimeos-core ../aimeos-core 46 | - run: cd .. && mv project aimeos-core/ext/ai-payments && mv aimeos-core project && cd project 47 | - restore_cache: 48 | keys: 49 | - php82-{{ checksum "composer.json" }} 50 | - run: composer config --no-plugins allow-plugins true 51 | - run: composer req --no-update "php-http/guzzle6-adapter" "omnipay/common:~3.0" "academe/omnipay-datatrans:~3.0" "omnipay/dummy:~3.0" "academe/omnipay-payone:~3.0" "omnipay/stripe:~3.0" 52 | - run: composer update -n --prefer-dist 53 | - save_cache: 54 | key: php82-{{ checksum "composer.json" }} 55 | paths: [./vendor] 56 | - run: echo " array( 'adapter' => 'mysql', 'host' => '127.0.0.1', 'port' => 3306, 'database' => 'aimeos', 'username' => 'aimeos', 'password' => 'aimeos', 'limit' => 2, 'opt-persistent' => false, 'stmt' => array( \"SET SESSIOn sort_buffer_size=2097144; SET NAMES 'utf8'; SET SESSION sql_mode='ANSI'\" ) ), 'fs' => array( 'adapter' => 'Standard', 'basedir' => '.' ), 'mq' => array( 'adapter' => 'Standard', 'db' => 'db' ) );" > config/resource.php 57 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done 58 | - run: ./vendor/bin/phing -Ddir=ext/ai-payments setup coverageext 59 | - run: bash <(curl -s https://codecov.io/bash) -n ${CIRCLE_BUILD_NUM} -t ${CODECOV_TOKEN} 60 | 61 | "php83-mysql": 62 | docker: 63 | - image: aimeos/ci-php:8.3 64 | - image: mysql:latest 65 | environment: 66 | MYSQL_ROOT_PASSWORD: rootpw 67 | MYSQL_DATABASE: aimeos 68 | MYSQL_USER: aimeos 69 | MYSQL_PASSWORD: aimeos 70 | steps: 71 | - checkout 72 | - run: git clone --depth=50 --branch=master https://github.com/aimeos/aimeos-core ../aimeos-core 73 | - run: cd .. && mv project aimeos-core/ext/ai-payments && mv aimeos-core project && cd project 74 | - restore_cache: 75 | keys: 76 | - php83-{{ checksum "composer.json" }} 77 | - run: composer config --no-plugins allow-plugins true 78 | - run: composer req --no-update "php-http/guzzle6-adapter" "omnipay/common:~3.0" "academe/omnipay-datatrans:~3.0" "omnipay/dummy:~3.0" "academe/omnipay-payone:~3.0" "omnipay/stripe:~3.0" 79 | - run: composer update -n --prefer-dist 80 | - save_cache: 81 | key: php83-{{ checksum "composer.json" }} 82 | paths: [./vendor] 83 | - run: echo " array( 'adapter' => 'mysql', 'host' => '127.0.0.1', 'port' => 3306, 'database' => 'aimeos', 'username' => 'aimeos', 'password' => 'aimeos', 'limit' => 2, 'opt-persistent' => false, 'stmt' => array( \"SET SESSIOn sort_buffer_size=2097144; SET NAMES 'utf8'; SET SESSION sql_mode='ANSI'\" ) ), 'fs' => array( 'adapter' => 'Standard', 'basedir' => '.' ), 'mq' => array( 'adapter' => 'Standard', 'db' => 'db' ) );" > config/resource.php 84 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done 85 | - run: ./vendor/bin/phing -Ddir=ext/ai-payments setup testext 86 | 87 | "php84-mysql": 88 | docker: 89 | - image: aimeos/ci-php:8.4 90 | - image: mysql:latest 91 | environment: 92 | MYSQL_ROOT_PASSWORD: rootpw 93 | MYSQL_DATABASE: aimeos 94 | MYSQL_USER: aimeos 95 | MYSQL_PASSWORD: aimeos 96 | steps: 97 | - checkout 98 | - run: git clone --depth=50 --branch=master https://github.com/aimeos/aimeos-core ../aimeos-core 99 | - run: cd .. && mv project aimeos-core/ext/ai-payments && mv aimeos-core project && cd project 100 | - restore_cache: 101 | keys: 102 | - php84-{{ checksum "composer.json" }} 103 | - run: composer config --no-plugins allow-plugins true 104 | - run: composer req --no-update "php-http/guzzle6-adapter" "omnipay/common:~3.0" "academe/omnipay-datatrans:~3.0" "omnipay/dummy:~3.0" "academe/omnipay-payone:~3.0" "omnipay/stripe:~3.0" 105 | - run: composer update -n --prefer-dist 106 | - save_cache: 107 | key: php84-{{ checksum "composer.json" }} 108 | paths: [./vendor] 109 | - run: echo " array( 'adapter' => 'mysql', 'host' => '127.0.0.1', 'port' => 3306, 'database' => 'aimeos', 'username' => 'aimeos', 'password' => 'aimeos', 'limit' => 2, 'opt-persistent' => false, 'stmt' => array( \"SET SESSIOn sort_buffer_size=2097144; SET NAMES 'utf8'; SET SESSION sql_mode='ANSI'\" ) ), 'fs' => array( 'adapter' => 'Standard', 'basedir' => '.' ), 'mq' => array( 'adapter' => 'Standard', 'db' => 'db' ) );" > config/resource.php 110 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done 111 | - run: ./vendor/bin/phing -Ddir=ext/ai-payments setup testext 112 | 113 | "php85-mysql": 114 | docker: 115 | - image: aimeos/ci-php:8.5 116 | - image: mysql:latest 117 | environment: 118 | MYSQL_ROOT_PASSWORD: rootpw 119 | MYSQL_DATABASE: aimeos 120 | MYSQL_USER: aimeos 121 | MYSQL_PASSWORD: aimeos 122 | steps: 123 | - checkout 124 | - run: git clone --depth=50 --branch=master https://github.com/aimeos/aimeos-core ../aimeos-core 125 | - run: cd .. && mv project aimeos-core/ext/ai-payments && mv aimeos-core project && cd project 126 | - restore_cache: 127 | keys: 128 | - php85-{{ checksum "composer.json" }} 129 | - run: composer config --no-plugins allow-plugins true 130 | - run: composer req --no-update "php-http/guzzle6-adapter" "omnipay/common:~3.0" "academe/omnipay-datatrans:~3.0" "omnipay/dummy:~3.0" "academe/omnipay-payone:~3.0" "omnipay/stripe:~3.0" 131 | - run: composer update -n --prefer-dist 132 | - save_cache: 133 | key: php85-{{ checksum "composer.json" }} 134 | paths: [./vendor] 135 | - run: echo " array( 'adapter' => 'mysql', 'host' => '127.0.0.1', 'port' => 3306, 'database' => 'aimeos', 'username' => 'aimeos', 'password' => 'aimeos', 'limit' => 2, 'opt-persistent' => false, 'stmt' => array( \"SET SESSIOn sort_buffer_size=2097144; SET NAMES 'utf8'; SET SESSION sql_mode='ANSI'\" ) ), 'fs' => array( 'adapter' => 'Standard', 'basedir' => '.' ), 'mq' => array( 'adapter' => 'Standard', 'db' => 'db' ) );" > config/resource.php 136 | - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done 137 | - run: ./vendor/bin/phing -Ddir=ext/ai-payments setup testext 138 | 139 | workflows: 140 | version: 2 141 | unittest: 142 | jobs: 143 | - "php81-mysql" 144 | - "php82-mysql" 145 | - "php83-mysql" 146 | - "php84-mysql" 147 | - "php85-mysql" -------------------------------------------------------------------------------- /tests/MShop/Service/Provider/Payment/DatatransTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped( 'Omnipay library not available' ); 23 | } 24 | 25 | $this->context = \TestHelper::context(); 26 | 27 | $serviceManager = \Aimeos\MShop::create( $this->context, 'service' ); 28 | $this->serviceItem = $serviceManager->create(); 29 | $this->serviceItem->setConfig( array( 'type' => 'Dummy' ) ); 30 | $this->serviceItem->setCode( 'unitpaymentcode' ); 31 | 32 | $methods = [ 33 | 'data', 'getTransactionReference', 'isImplemented', 34 | 'save', 'getProvider', 'getXmlProvider', 'setOrderData', 'setData' 35 | ]; 36 | 37 | $this->object = $this->getMockBuilder( \Aimeos\MShop\Service\Provider\Payment\Datatrans::class ) 38 | ->setConstructorArgs( array( $this->context, $this->serviceItem ) ) 39 | ->onlyMethods( $methods ) 40 | ->getMock(); 41 | } 42 | 43 | 44 | protected function tearDown() : void 45 | { 46 | unset( $this->object, $this->serviceItem, $this->context ); 47 | } 48 | 49 | 50 | public function testGetConfigBE() 51 | { 52 | $result = $this->object->getConfigBE(); 53 | 54 | $this->assertIsArray( $result ); 55 | $this->assertArrayHasKey( 'password', $result ); 56 | $this->assertArrayHasKey( 'address', $result ); 57 | $this->assertArrayHasKey( 'authorize', $result ); 58 | $this->assertArrayHasKey( 'testmode', $result ); 59 | $this->assertArrayHasKey( 'createtoken', $result ); 60 | $this->assertArrayHasKey( 'onsite', $result ); 61 | $this->assertArrayHasKey( 'type', $result ); 62 | } 63 | 64 | 65 | public function testCheckConfigBE() 66 | { 67 | $attributes = array( 68 | 'address' => '0', 69 | 'authorize' => '1', 70 | 'testmode' => '1', 71 | 'password' => 'test', 72 | 'type' => 'Datatrans', 73 | 'merchantId' => 'abdc', 74 | 'sign' => '12345', 75 | ); 76 | 77 | $result = $this->object->checkConfigBE( $attributes ); 78 | 79 | $this->assertEquals( 10, count( $result ) ); 80 | $this->assertEquals( null, $result['password'] ); 81 | $this->assertEquals( null, $result['address'] ); 82 | $this->assertEquals( null, $result['authorize'] ); 83 | $this->assertEquals( null, $result['createtoken'] ); 84 | $this->assertEquals( null, $result['testmode'] ); 85 | $this->assertEquals( null, $result['onsite'] ); 86 | $this->assertEquals( null, $result['type'] ); 87 | $this->assertEquals( null, $result['sign'] ); 88 | $this->assertEquals( null, $result['hmacKey1'] ); 89 | $this->assertEquals( null, $result['merchantId'] ); 90 | } 91 | 92 | 93 | public function testQuerySuccess() 94 | { 95 | $provider = $this->getMockBuilder( 'Omnipay\Dummy\Gateway' ) 96 | ->addMethods( ['getTransaction'] ) 97 | ->getMock(); 98 | 99 | $request = $this->getMockBuilder( \Omnipay\Common\Message\AbstractRequest::class ) 100 | ->disableOriginalConstructor() 101 | ->getMock(); 102 | 103 | $response = $this->getMockBuilder( 'Omnipay\Dummy\Message\Response' ) 104 | ->disableOriginalConstructor() 105 | ->onlyMethods( ['isSuccessful', 'getTransactionReference'] ) 106 | ->addMethods( ['getResponseCode'] ) 107 | ->getMock(); 108 | 109 | $this->object->expects( $this->once() )->method( 'getProvider' ) 110 | ->willReturn( $provider ); 111 | 112 | $provider->expects( $this->once() )->method( 'getTransaction' ) 113 | ->willReturn( $request ); 114 | 115 | $request->expects( $this->once() )->method( 'send' ) 116 | ->willReturn( $response ); 117 | 118 | $response->expects( $this->once() )->method( 'isSuccessful' ) 119 | ->willReturn( true ); 120 | 121 | $response->expects( $this->once() )->method( 'getTransactionReference' ) 122 | ->willReturn( '' ); 123 | 124 | $this->object->expects( $this->once() )->method( 'setOrderData' ); 125 | 126 | $order = $this->object->query( $this->getOrder() ); 127 | 128 | $this->assertEquals( \Aimeos\MShop\Order\Item\Base::PAY_RECEIVED, $order->getStatusPayment() ); 129 | } 130 | 131 | 132 | public function testQueryAuthorizeFailure() 133 | { 134 | $this->serviceItem->setConfig( array( 'type' => 'Dummy', 'authorize' => '1' ) ); 135 | 136 | $provider = $this->getMockBuilder( \Omnipay\Dummy\Gateway::class ) 137 | ->onlyMethods( ['supportsCompleteAuthorize', 'completeAuthorize'] ) 138 | ->addMethods( ['getTransaction'] ) 139 | ->disableOriginalConstructor() 140 | ->getMock(); 141 | 142 | $request = $this->getMockBuilder( \Omnipay\Common\Message\AbstractRequest::class ) 143 | ->disableOriginalConstructor() 144 | ->getMock(); 145 | 146 | $response = $this->getMockBuilder( 'Omnipay\Dummy\Message\Response' ) 147 | ->disableOriginalConstructor() 148 | ->onlyMethods( ['isSuccessful', 'getTransactionReference'] ) 149 | ->addMethods( ['getResponseCode'] ) 150 | ->getMock(); 151 | 152 | $this->object->expects( $this->once() )->method( 'getProvider' ) 153 | ->willReturn( $provider ); 154 | 155 | $provider->expects( $this->once() )->method( 'getTransaction' ) 156 | ->willReturn( $request ); 157 | 158 | $request->expects( $this->once() )->method( 'send' ) 159 | ->willReturn( $response ); 160 | 161 | $response->expects( $this->once() )->method( 'isSuccessful' ) 162 | ->willReturn( false ); 163 | 164 | $response->expects( $this->once() )->method( 'getTransactionReference' ) 165 | ->willReturn( '' ); 166 | 167 | $this->object->query( $this->getOrder() ); 168 | } 169 | 170 | 171 | public function testQueryPending() 172 | { 173 | $provider = $this->getMockBuilder( 'Omnipay\Dummy\Gateway' ) 174 | ->addMethods( ['getTransaction'] ) 175 | ->getMock(); 176 | 177 | $request = $this->getMockBuilder( \Omnipay\Common\Message\AbstractRequest::class ) 178 | ->disableOriginalConstructor() 179 | ->getMock(); 180 | 181 | $response = $this->getMockBuilder( 'Omnipay\Dummy\Message\Response' ) 182 | ->disableOriginalConstructor() 183 | ->onlyMethods( ['isPending', 'getTransactionReference'] ) 184 | ->getMock(); 185 | 186 | $this->object->expects( $this->once() )->method( 'getProvider' ) 187 | ->willReturn( $provider ); 188 | 189 | $provider->expects( $this->once() )->method( 'getTransaction' ) 190 | ->willReturn( $request ); 191 | 192 | $request->expects( $this->once() )->method( 'send' ) 193 | ->willReturn( $response ); 194 | 195 | $response->expects( $this->once() )->method( 'isPending' ) 196 | ->willReturn( true ); 197 | 198 | $response->expects( $this->once() )->method( 'getTransactionReference' ) 199 | ->willReturn( '' ); 200 | 201 | $order = $this->object->query( $this->getOrder() ); 202 | 203 | $this->assertEquals( \Aimeos\MShop\Order\Item\Base::PAY_PENDING, $order->getStatusPayment() ); 204 | } 205 | 206 | 207 | public function testQueryCancelled() 208 | { 209 | $provider = $this->getMockBuilder( 'Omnipay\Dummy\Gateway' ) 210 | ->addMethods( ['getTransaction'] ) 211 | ->getMock(); 212 | 213 | $request = $this->getMockBuilder( \Omnipay\Common\Message\AbstractRequest::class ) 214 | ->disableOriginalConstructor() 215 | ->getMock(); 216 | 217 | $response = $this->getMockBuilder( 'Omnipay\Dummy\Message\Response' ) 218 | ->disableOriginalConstructor() 219 | ->onlyMethods( ['isCancelled', 'getTransactionReference'] ) 220 | ->getMock(); 221 | 222 | $this->object->expects( $this->once() )->method( 'getProvider' ) 223 | ->willReturn( $provider ); 224 | 225 | $provider->expects( $this->once() )->method( 'getTransaction' ) 226 | ->willReturn( $request ); 227 | 228 | $request->expects( $this->once() )->method( 'send' ) 229 | ->willReturn( $response ); 230 | 231 | $response->expects( $this->once() )->method( 'isCancelled' ) 232 | ->willReturn( true ); 233 | 234 | $response->expects( $this->once() )->method( 'getTransactionReference' ) 235 | ->willReturn( '' ); 236 | 237 | $order = $this->object->query( $this->getOrder() ); 238 | 239 | $this->assertEquals( \Aimeos\MShop\Order\Item\Base::PAY_CANCELED, $order->getStatusPayment() ); 240 | } 241 | 242 | 243 | public function testRepay() 244 | { 245 | $orderItem = $this->getOrder(); 246 | 247 | $provider = $this->getMockBuilder( 'Omnipay\Dummy\Gateway' ) 248 | ->onlyMethods( ['purchase'] ) 249 | ->addMethods( ['getCard'] ) 250 | ->getMock(); 251 | 252 | $request = $this->getMockBuilder( \Omnipay\Common\Message\AbstractRequest::class ) 253 | ->disableOriginalConstructor() 254 | ->getMock(); 255 | 256 | $response = $this->getMockBuilder( 'Omnipay\Dummy\Message\Response' ) 257 | ->disableOriginalConstructor() 258 | ->onlyMethods( array( 'isSuccessful', 'getTransactionReference' ) ) 259 | ->getMock(); 260 | 261 | 262 | $this->object->expects( $this->once() )->method( 'getXmlProvider' ) 263 | ->willReturn( $provider ); 264 | 265 | $this->object->expects( $this->once() )->method( 'data' ) 266 | ->willReturn( ['token' => '123', 'month' => '01', 'year' => '99'] ); 267 | 268 | $provider->expects( $this->once() )->method( 'purchase' ) 269 | ->willReturn( $request ); 270 | 271 | $request->expects( $this->once() )->method( 'send' ) 272 | ->willReturn( $response ); 273 | 274 | $response->expects( $this->once() )->method( 'isSuccessful' ) 275 | ->willReturn( true ); 276 | 277 | $response->expects( $this->once() )->method( 'getTransactionReference' ) 278 | ->willReturn( '' ); 279 | 280 | $this->object->expects( $this->once() )->method( 'setOrderData' ); 281 | 282 | 283 | $this->object->repay( $this->getOrder() ); 284 | } 285 | 286 | 287 | public function testRepayMissingData() 288 | { 289 | $this->object->expects( $this->once() )->method( 'data' ) 290 | ->willReturn( null ); 291 | 292 | 293 | $this->expectException( \Aimeos\MShop\Service\Exception::class ); 294 | $this->object->repay( $this->getOrder() ); 295 | } 296 | 297 | 298 | public function testRepayMissingToken() 299 | { 300 | $this->object->expects( $this->once() )->method( 'data' ) 301 | ->willReturn( [] ); 302 | 303 | 304 | $this->expectException( \Aimeos\MShop\Service\Exception::class ); 305 | $this->object->repay( $this->getOrder() ); 306 | } 307 | 308 | 309 | public function testGetValue() 310 | { 311 | $this->assertEquals( 'Datatrans', $this->access( 'getValue' )->invokeArgs( $this->object, ['type'] ) ); 312 | $this->assertEquals( null, $this->access( 'getValue' )->invokeArgs( $this->object, ['test'] ) ); 313 | } 314 | 315 | 316 | public function testGetXmlProvider() 317 | { 318 | $result = $this->access( 'getXmlProvider' )->invokeArgs( $this->object, [] ); 319 | $this->assertInstanceOf( \Omnipay\Common\GatewayInterface::class, $result ); 320 | } 321 | 322 | 323 | protected function getOrder() 324 | { 325 | $manager = \Aimeos\MShop::create( $this->context, 'order' ); 326 | $search = $manager->filter()->add( 'order.datepayment', '==', '2008-02-15 12:34:56' ); 327 | 328 | return $manager->search( $search, ['order', 'order/service'] ) 329 | ->first( new \RuntimeException( 'No order found' ) ); 330 | } 331 | 332 | 333 | protected function access( $name ) 334 | { 335 | $class = new \ReflectionClass( \Aimeos\MShop\Service\Provider\Payment\Datatrans::class ); 336 | $method = $class->getMethod( $name ); 337 | $method->setAccessible( true ); 338 | 339 | return $method; 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /src/MShop/Service/Provider/Payment/PaypalPlus.php: -------------------------------------------------------------------------------- 1 | array( 29 | 'code' => 'authorize', 30 | 'internalcode'=> 'authorize', 31 | 'label'=> 'Authorize payments and capture later', 32 | 'type' => 'bool', 33 | 'internaltype'=> 'boolean', 34 | 'default'=> '0', 35 | 'required'=> false, 36 | ), 37 | 'clientid' => array( 38 | 'code' => 'clientid', 39 | 'internalcode' => 'clientid', 40 | 'label' => 'Client ID for PayPal REST API', 41 | 'type' => 'string', 42 | 'internaltype' => 'string', 43 | 'default' => '0', 44 | 'required' => true, 45 | ), 46 | 'secret' => array( 47 | 'code' => 'secret', 48 | 'internalcode' => 'secret', 49 | 'label' => 'Secret for PayPal REST API', 50 | 'type' => 'string', 51 | 'internaltype' => 'string', 52 | 'default' => '0', 53 | 'required' => true, 54 | ), 55 | 'testmode' => array( 56 | 'code' => 'testmode', 57 | 'internalcode' => 'testmode', 58 | 'label' => 'Test mode without payments', 59 | 'type' => 'bool', 60 | 'internaltype' => 'boolean', 61 | 'default' => '0', 62 | 'required' => true, 63 | ), 64 | ); 65 | 66 | private $provider; 67 | 68 | 69 | /** 70 | * Checks the backend configuration attributes for validity. 71 | * 72 | * @param array $attributes Attributes added by the shop owner in the administraton interface 73 | * @return array An array with the attribute keys as key and an error message as values for all attributes that are 74 | * known by the provider but aren't valid 75 | */ 76 | public function checkConfigBE( array $attributes ) : array 77 | { 78 | return $this->checkConfig( $this->beConfig, $attributes ); 79 | } 80 | 81 | 82 | /** 83 | * Returns the configuration attribute definitions of the provider to generate a list of available fields and 84 | * rules for the value of each field in the administration interface. 85 | * 86 | * @return array List of attribute definitions implementing \Aimeos\Base\Critera\Attribute\Iface 87 | */ 88 | public function getConfigBE() : array 89 | { 90 | $list = []; 91 | 92 | foreach( $this->beConfig as $key => $config ) { 93 | $list[$key] = new \Aimeos\Base\Criteria\Attribute\Standard( $config ); 94 | } 95 | 96 | return $list; 97 | } 98 | 99 | 100 | /** 101 | * Returns the Omnipay gateway provider object. 102 | * 103 | * @return \Omnipay\Common\GatewayInterface Gateway provider object 104 | */ 105 | protected function getProvider() : \Omnipay\Common\GatewayInterface 106 | { 107 | if( !isset( $this->provider ) ) 108 | { 109 | $this->provider = OPay::create( 'PayPal_Rest' ); 110 | $this->provider->setTestMode( (bool) $this->getValue( 'testmode', false ) ); 111 | $this->provider->initialize( $this->getServiceItem()->getConfig() ); 112 | } 113 | 114 | return $this->provider; 115 | } 116 | 117 | 118 | /** 119 | * Tries to get an authorization or captures the money immediately for the given order if capturing the money 120 | * separately isn't supported or not configured by the shop owner. 121 | * 122 | * @param \Aimeos\MShop\Order\Item\Iface $order Order invoice object 123 | * @param array $params Request parameter if available 124 | * @return \Aimeos\MShop\Common\Helper\Form\Iface|null Form object with URL, action and parameters to redirect to 125 | * (e.g. to an external server of the payment provider or to a local success page) 126 | */ 127 | public function process( \Aimeos\MShop\Order\Item\Iface $order, array $params = [] ) : ?\Aimeos\MShop\Common\Helper\Form\Iface 128 | { 129 | $addresses = $order->getAddress( \Aimeos\MShop\Order\Item\Address\Base::TYPE_PAYMENT ); 130 | 131 | $data = $this->getData( $order, $order->getId(), $params ); 132 | $response = $this->sendRequest( $order, $data ); 133 | 134 | if( !$response->isSuccessful() ) 135 | { 136 | \Aimeos\MShop::create( $this->context(), 'order' )->save( $order->setStatusPayment( Status::PAY_REFUSED ) ); 137 | throw new \Aimeos\MShop\Service\Exception( (string) $response->getMessage() ); 138 | } 139 | 140 | $approvalUrl = ''; 141 | $addresses = $order->getAddress( \Aimeos\MShop\Order\Item\Address\Base::TYPE_PAYMENT ); 142 | 143 | $this->setOrderData( $order, ['Transaction' => $response->getTransactionReference()] ); 144 | $this->saveRepayData( $response, $order->getCustomerId() ); 145 | 146 | foreach( $response->getData()['links'] ?? [] as $entry ) 147 | { 148 | if( $entry['rel'] === 'approval_url' ) { 149 | $approvalUrl = $entry['href']; 150 | } 151 | } 152 | 153 | if( empty( $approvalUrl ) ) 154 | { 155 | $msg = $this->context()->translate( 'mshop', 'PayPalPlus approval URL not available' ); 156 | throw new \Aimeos\MShop\Service\Exception( $msg ); 157 | } 158 | 159 | if( ( $address = current( $addresses ) ) === false ) 160 | { 161 | $msg = $this->context()->translate( 'mshop', 'PayPalPlus requires the country ID of the user' ); 162 | throw new \Aimeos\MShop\Service\Exception( $msg ); 163 | } 164 | 165 | $langid = $address->getLanguageId() ?: $this->context()->locale()->getLanguageId(); 166 | 167 | $html = $this->getPayPalPlusJs( $approvalUrl, (string) $address->getCountryId(), (string) $langid ); 168 | return new \Aimeos\MShop\Common\Helper\Form\Standard( '', '', [], true, $html ); 169 | } 170 | 171 | 172 | /** 173 | * Updates the orders for whose status updates have been received by the confirmation page 174 | * 175 | * @param \Psr\Http\Message\ServerRequestInterface $request Request object with parameters and request body 176 | * @param \Aimeos\MShop\Order\Item\Iface $order Order item that should be updated 177 | * @return \Aimeos\MShop\Order\Item\Iface Updated order item 178 | * @throws \Aimeos\MShop\Service\Exception If updating the orders failed 179 | */ 180 | public function updateSync( \Psr\Http\Message\ServerRequestInterface $request, 181 | \Aimeos\MShop\Order\Item\Iface $order ) : \Aimeos\MShop\Order\Item\Iface 182 | { 183 | if( empty( $request->getQueryParams()['PayerID'] ) ) { 184 | return $order->setStatusPayment( Status::PAY_CANCELED ); 185 | } 186 | 187 | try 188 | { 189 | $provider = $this->getProvider(); 190 | 191 | $params = (array) $request->getAttributes() + (array) $request->getParsedBody() + (array) $request->getQueryParams(); 192 | $params = $this->getData( $order, $order->getId(), $params ); 193 | $params['transactionReference'] = $this->getTransactionReference( $order ); 194 | 195 | if( $this->getValue( 'authorize', false ) && $provider->supportsCompleteAuthorize() ) 196 | { 197 | $response = $provider->completeAuthorize( $params )->send(); 198 | $status = Status::PAY_AUTHORIZED; 199 | } 200 | elseif( $provider->supportsCompletePurchase() ) 201 | { 202 | $response = $provider->completePurchase( $params )->send(); 203 | $status = Status::PAY_RECEIVED; 204 | } 205 | else 206 | { 207 | return $order; 208 | } 209 | 210 | // next command that get TransactionID was $response->getTransactionId() but it doesn't work 211 | if( $response->getRequest()->getTransactionId() != $order->getId() ) { 212 | return $order; 213 | } 214 | 215 | if( method_exists( $response, 'isSuccessful' ) && $response->isSuccessful() ) 216 | { 217 | $order->setStatusPayment( $status ); 218 | } 219 | elseif( method_exists( $response, 'isPending' ) && $response->isPending() ) 220 | { 221 | $order->setStatusPayment( Status::PAY_PENDING ); 222 | } 223 | elseif( method_exists( $response, 'isCancelled' ) && $response->isCancelled() ) 224 | { 225 | $order->setStatusPayment( Status::PAY_CANCELED ); 226 | } 227 | elseif( method_exists( $response, 'isRedirect' ) && $response->isRedirect() ) 228 | { 229 | $msg = $this->context()->translate( 'mshop', 'Unexpected redirect: %1$s' ); 230 | throw new \Aimeos\MShop\Service\Exception( sprintf( $msg, $response->getRedirectUrl() ) ); 231 | } 232 | else 233 | { 234 | if( $order->getStatusPayment() === Status::PAY_UNFINISHED ) { 235 | \Aimeos\MShop::create( $this->context(), 'order' )->save( $order->setStatusPayment( Status::PAY_REFUSED ) ); 236 | } 237 | 238 | throw new \Aimeos\MShop\Service\Exception( (string) $response->getMessage() ); 239 | } 240 | 241 | $this->setOrderData( $order, ['Transaction' => $response->getTransactionReference()] ); 242 | $this->saveRepayData( $response, $order->getCustomerId() ); 243 | } 244 | catch( \Exception $e ) 245 | { 246 | throw new \Aimeos\MShop\Service\Exception( $e->getMessage() ); 247 | } 248 | 249 | return $order; 250 | } 251 | 252 | 253 | /** 254 | * Returns the data passed to the Omnipay library 255 | * 256 | * @param \Aimeos\MShop\Order\Item\Iface $order Basket object 257 | * @param string $orderid string Unique order ID 258 | * @param array $params Request parameter if available 259 | * @return array Associative list of key/value pairs 260 | */ 261 | protected function getData( \Aimeos\MShop\Order\Item\Iface $order, string $orderid, array $params ) : array 262 | { 263 | return ['PayerID' => $params['PayerID'] ?? null] + parent::getData( $order, $orderid, $params ); 264 | } 265 | 266 | 267 | /** 268 | * Returns the HTML code for displaying the PayPalPlus form. 269 | * 270 | * @param string $approvalUrl Approval URL sent by PayPalPlus 271 | * @param string $countryid Two letter ISO country code 272 | * @param string $languageid Two letter ISO language code 273 | * @return string HTML code 274 | */ 275 | protected function getPayPalPlusJs( string $approvalUrl, string $countryid, string $languageid ) : string 276 | { 277 | return ' 278 |
279 | 280 | '; 292 | } 293 | 294 | 295 | /** 296 | * Sends the given data for the order to the payment gateway 297 | * 298 | * @param \Aimeos\MShop\Order\Item\Iface $order Order item which should be paid 299 | * @param array $data Associative list of key/value pairs sent to the payment gateway 300 | * @return \Omnipay\Common\Message\ResponseInterface Omnipay response from the payment gateway 301 | */ 302 | protected function sendRequest( \Aimeos\MShop\Order\Item\Iface $order, array $data ) : \Omnipay\Common\Message\ResponseInterface 303 | { 304 | $provider = $this->getProvider(); 305 | 306 | if( $this->getValue( 'authorize', false ) && $provider->supportsAuthorize() ) { 307 | $response = $provider->authorize( $data )->send(); 308 | } else { 309 | $response = $provider->purchase( $data )->send(); 310 | } 311 | 312 | return $response; 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /src/MShop/Service/Provider/Payment/Stripe.php: -------------------------------------------------------------------------------- 1 | array( 28 | 'code' => 'type', 29 | 'internalcode'=> 'type', 30 | 'label'=> 'Payment provider type', 31 | 'type'=> 'string', 32 | 'internaltype'=> 'string', 33 | 'default'=> 'Stripe_PaymentIntents', 34 | 'required'=> true, 35 | ), 36 | 'apiKey' => array( 37 | 'code' => 'apiKey', 38 | 'internalcode'=> 'apiKey', 39 | 'label'=> 'API key', 40 | 'type'=> 'string', 41 | 'internaltype'=> 'string', 42 | 'default'=> '', 43 | 'required'=> true, 44 | ), 45 | 'publishableKey' => array( 46 | 'code' => 'publishableKey', 47 | 'internalcode'=> 'publishableKey', 48 | 'label'=> 'Publishable key', 49 | 'type'=> 'string', 50 | 'internaltype'=> 'string', 51 | 'default'=> '', 52 | 'required'=> true, 53 | ), 54 | ); 55 | 56 | protected $feConfig = array( 57 | 'paymenttoken' => array( 58 | 'code' => 'paymenttoken', 59 | 'internalcode' => 'paymenttoken', 60 | 'label' => 'Authentication token', 61 | 'type' => 'string', 62 | 'internaltype' => 'integer', 63 | 'default' => '', 64 | 'required' => true, 65 | 'public' => false, 66 | ), 67 | 'payment.cardno' => array( 68 | 'code' => 'payment.cardno', 69 | 'internalcode'=> 'number', 70 | 'label'=> 'Credit card number', 71 | 'type'=> 'container', 72 | 'internaltype'=> 'integer', 73 | 'default'=> '', 74 | 'required'=> false 75 | ), 76 | 'payment.expiry' => array( 77 | 'code' => 'payment.expiry', 78 | 'internalcode'=> 'expiry', 79 | 'label'=> 'Expiry', 80 | 'type'=> 'container', 81 | 'internaltype'=> 'string', 82 | 'default'=> '', 83 | 'required'=> false 84 | ), 85 | 'payment.cvv' => array( 86 | 'code' => 'payment.cvv', 87 | 'internalcode'=> 'cvv', 88 | 'label'=> 'Verification number', 89 | 'type'=> 'container', 90 | 'internaltype'=> 'integer', 91 | 'default'=> '', 92 | 'required'=> false 93 | ), 94 | ); 95 | 96 | 97 | /** 98 | * Checks the backend configuration attributes for validity. 99 | * 100 | * @param array $attributes Attributes added by the shop owner in the administraton interface 101 | * @return array An array with the attribute keys as key and an error message as values for all attributes that are 102 | * known by the provider but aren't valid resp. null for attributes whose values are OK 103 | */ 104 | public function checkConfigBE( array $attributes ) : array 105 | { 106 | return array_merge( parent::checkConfigBE( $attributes ), $this->checkConfig( $this->beConfig, $attributes ) ); 107 | } 108 | 109 | 110 | /** 111 | * Returns the configuration attribute definitions of the provider to generate a list of available fields and 112 | * rules for the value of each field in the administration interface. 113 | * 114 | * @return array List of attribute definitions implementing \Aimeos\Base\Critera\Attribute\Iface 115 | */ 116 | public function getConfigBE() : array 117 | { 118 | $list = parent::getConfigBE(); 119 | 120 | foreach( $this->beConfig as $key => $config ) { 121 | $list[$key] = new \Aimeos\Base\Criteria\Attribute\Standard( $config ); 122 | } 123 | 124 | return $list; 125 | } 126 | 127 | 128 | /** 129 | * Tries to get an authorization or captures the money immediately for the given order if capturing the money 130 | * separately isn't supported or not configured by the shop owner. 131 | * 132 | * @param \Aimeos\MShop\Order\Item\Iface $order Order invoice object 133 | * @param array $params Request parameter if available 134 | * @return \Aimeos\MShop\Common\Helper\Form\Iface|null Form object with URL, action and parameters to redirect to 135 | * (e.g. to an external server of the payment provider or to a local success page) 136 | */ 137 | public function process( \Aimeos\MShop\Order\Item\Iface $order, array $params = [] ) : ?\Aimeos\MShop\Common\Helper\Form\Iface 138 | { 139 | if( !isset( $params['paymenttoken'] ) ) { 140 | return $this->getPaymentForm( $order, $params ); 141 | } 142 | 143 | if( ( $userid = $this->context()->user() ) !== null 144 | && $this->data( $userid, 'customer' ) === null 145 | && $this->getConfigValue( 'createtoken' ) 146 | ) { 147 | $data = []; 148 | 149 | if( $addr = current( $order->getAddress( 'payment' ) ) ) 150 | { 151 | $data['description'] = $addr->getFirstName() . ' ' . $addr->getLastName(); 152 | $data['email'] = $addr->getEmail(); 153 | } 154 | 155 | $response = $this->getProvider()->createCustomer( $data )->send(); 156 | 157 | if( $response->isSuccessful() ) { 158 | $this->setData( $userid, 'customer', $response->getCustomerReference() ); 159 | } 160 | } 161 | 162 | return $this->processOrder( $order, $params ); 163 | } 164 | 165 | 166 | /** 167 | * Executes the payment again for the given order if supported. 168 | * This requires support of the payment gateway and token based payment 169 | * 170 | * @param \Aimeos\MShop\Order\Item\Iface $order Order invoice object 171 | * @return \Aimeos\MShop\Order\Item\Iface Updated order item object 172 | */ 173 | public function repay( \Aimeos\MShop\Order\Item\Iface $order ) : \Aimeos\MShop\Order\Item\Iface 174 | { 175 | if( ( $custid = $this->data( $order->getCustomerId(), 'customer' ) ) === null ) 176 | { 177 | $msg = sprintf( 'No Stripe customer data available for customer ID "%1$s"', $order->getCustomerId() ); 178 | throw new \Aimeos\MShop\Service\Exception( $msg ); 179 | } 180 | 181 | if( ( $cfg = $this->data( $order->getCustomerId(), 'repay' ) ) === null ) 182 | { 183 | $msg = sprintf( 'No Stripe payment method available for customer ID "%1$s"', $order->getCustomerId() ); 184 | throw new \Aimeos\MShop\Service\Exception( $msg ); 185 | } 186 | 187 | if( !isset( $cfg['token'] ) ) 188 | { 189 | $msg = sprintf( 'No payment token available for customer ID "%1$s"', $order->getCustomerId() ); 190 | throw new \Aimeos\MShop\Service\Exception( $msg ); 191 | } 192 | 193 | $response = $this->getProvider()->purchase( [ 194 | 'transactionId' => $order->getId(), 195 | 'currency' => $order->getPrice()->getCurrencyId(), 196 | 'amount' => $this->getAmount( $order->getPrice() ), 197 | 'cardReference' => $cfg['token'], 198 | 'customerReference' => $custid, 199 | 'off_session' => true, 200 | 'confirm' => true, 201 | ] )->send(); 202 | 203 | if( $response->isSuccessful() || $response->isPending() ) 204 | { 205 | $this->setOrderData( $order, ['Transaction' => $response->getTransactionReference()] ); 206 | $order = $order->setStatusPayment( Status::PAY_RECEIVED ); 207 | } 208 | elseif( !$response->getTransactionReference() ) 209 | { 210 | $msg = $this->context()->translate( 'mshop', 'Token based payment incomplete: %1$s' ); 211 | throw new \Aimeos\MShop\Service\Exception( sprintf( $msg, print_r( $response->getData(), true ) ), 1 ); 212 | } 213 | else 214 | { 215 | $str = ( method_exists( $response, 'getMessage' ) ? $response->getMessage() : '' ); 216 | $msg = $this->context()->translate( 'mshop', 'Token based payment failed: %1$s' ); 217 | throw new \Aimeos\MShop\Service\Exception( sprintf( $msg, $str ), -1 ); 218 | } 219 | 220 | return $order; 221 | } 222 | 223 | 224 | /** 225 | * Updates the orders for whose status updates have been received by the confirmation page 226 | * 227 | * @param \Psr\Http\Message\ServerRequestInterface $request Request object with parameters and request body 228 | * @param \Aimeos\MShop\Order\Item\Iface $order Order item that should be updated 229 | * @return \Aimeos\MShop\Order\Item\Iface Updated order item 230 | * @throws \Aimeos\MShop\Service\Exception If updating the orders failed 231 | */ 232 | public function updateSync( \Psr\Http\Message\ServerRequestInterface $request, 233 | \Aimeos\MShop\Order\Item\Iface $order ) : \Aimeos\MShop\Order\Item\Iface 234 | { 235 | if( $order->getStatusPayment() === Status::PAY_UNFINISHED ) 236 | { 237 | $response = $this->getProvider()->confirm( [ 238 | 'paymentIntentReference' => $this->getOrderData( $order, 'Reference' ), 239 | 'return_url' => $this->getConfigValue( ['payment.url-self'] ), 240 | ] )->send(); 241 | 242 | if( $response->isSuccessful() ) 243 | { 244 | $status = $this->getValue( 'authorize', false ) ? Status::PAY_AUTHORIZED : Status::PAY_RECEIVED; 245 | $this->setOrderData( $order, ['Transaction' => $response->getTransactionReference()] ); 246 | 247 | $user = $this->context()->user(); 248 | $paymethod = $response->getCardReference(); 249 | 250 | if( $user && $paymethod ) { 251 | $this->setData( $user, 'repay', ['token' => $paymethod] ); 252 | } 253 | } 254 | else 255 | { 256 | $status = Status::PAY_REFUSED; 257 | } 258 | 259 | $order->setStatusPayment( $status ); 260 | } 261 | 262 | return $order; 263 | } 264 | 265 | 266 | /** 267 | * Returns the data sent to the payment gateway for capturing 268 | * 269 | * @param \Aimeos\MShop\Order\Item\Iface $order Order item 270 | * @return array Associative list of key/value pairs 271 | */ 272 | protected function captureData( \Aimeos\MShop\Order\Item\Iface $order ) : array 273 | { 274 | $map = parent::captureData( $order ); 275 | $map['paymentIntentReference'] = $this->service( $order )->getAttribute( 'Reference', 'payment/omnipay' ); 276 | 277 | return $map; 278 | } 279 | 280 | 281 | /** 282 | * Returns the data passed to the Omnipay library 283 | * 284 | * @param \Aimeos\MShop\Order\Item\Iface $order Basket object 285 | * @param string $orderid Unique order ID 286 | * @param array $params Request parameter if available 287 | * @return array Associative list of key/value pairs 288 | */ 289 | protected function getData( \Aimeos\MShop\Order\Item\Iface $order, string $orderid, array $params ) : array 290 | { 291 | $session = $this->context()->session(); 292 | $data = parent::getData( $order, $orderid, $params ); 293 | 294 | if( isset( $params['paymenttoken'] ) ) { 295 | $session->set( 'aimeos/stripe_token', $params['paymenttoken'] ); 296 | } 297 | 298 | if( ( $token = $session->get( 'aimeos/stripe_token' ) ) !== null ) { 299 | $data['token'] = $token; 300 | } 301 | 302 | if( $this->context()->user() && $this->getConfigValue( 'createtoken' ) 303 | && $custid = $this->data( $this->context()->user(), 'customer' ) 304 | ) { 305 | $data['customerReference'] = $custid; 306 | } 307 | 308 | $type = \Aimeos\MShop\Order\Item\Service\Base::TYPE_PAYMENT; 309 | $serviceItem = $this->getBasketService( $order, $type, $this->getServiceItem()->getCode() ); 310 | 311 | if( $stripeIntentsRef = $serviceItem->getAttribute( 'Reference', 'payment/omnipay' ) ) { 312 | $data['paymentIntentReference'] = $stripeIntentsRef; 313 | } 314 | 315 | $data['transferGroup'] = 'ORDER-' . $orderid; 316 | $data['confirm'] = true; 317 | 318 | return $data + $this->getPaymentUrls(); 319 | } 320 | 321 | 322 | /** 323 | * Returns the payment form for entering payment details at the shop site. 324 | * 325 | * @param \Aimeos\MShop\Order\Item\Iface $order Order object 326 | * @param array $params Request parameter if available 327 | * @return \Aimeos\MShop\Common\Helper\Form\Iface Form helper object 328 | */ 329 | protected function getPaymentForm( \Aimeos\MShop\Order\Item\Iface $order, array $params ) : \Aimeos\MShop\Common\Helper\Form\Iface 330 | { 331 | $list = []; 332 | $feConfig = $this->feConfig; 333 | 334 | foreach( $feConfig as $key => $config ) { 335 | $list[$key] = new \Aimeos\Base\Criteria\Attribute\Standard( $config ); 336 | } 337 | 338 | $url = $this->getConfigValue( 'payment.url-self', '' ); 339 | return new \Aimeos\MShop\Common\Helper\Form\Standard( $url, 'POST', $list, false, $this->getStripeJs() ); 340 | } 341 | 342 | 343 | /** 344 | * Returns the required Javascript code for Stripe payment form 345 | * 346 | * @return string Stripe JS code 347 | */ 348 | protected function getStripeJs() : string 349 | { 350 | return ' 351 | 352 | 433 | 434 | 435 | '; 436 | } 437 | 438 | 439 | /** 440 | * Sends the given data for the order to the payment gateway 441 | * 442 | * @param \Aimeos\MShop\Order\Item\Iface $order Order item which should be paid 443 | * @param array $data Associative list of key/value pairs sent to the payment gateway 444 | * @return \Omnipay\Common\Message\ResponseInterface Omnipay response from the payment gateway 445 | */ 446 | protected function sendRequest( \Aimeos\MShop\Order\Item\Iface $order, array $data ) : \Omnipay\Common\Message\ResponseInterface 447 | { 448 | if( $this->getConfigValue( 'createtoken' ) ) { 449 | $data['setup_future_usage'] = 'off_session'; 450 | } 451 | 452 | $response = parent::sendRequest( $order, $data ); 453 | 454 | if( method_exists( $response, 'getPaymentIntentReference' ) ) { 455 | $this->setOrderData( $order, ['Reference' => $response->getPaymentIntentReference()] ); 456 | } 457 | 458 | return $response; 459 | } 460 | 461 | 462 | /** 463 | * Returns the Stripe service 464 | * 465 | * @param \Aimeos\MShop\Order\Item\Iface $basket Basket object with services 466 | * @return \Aimeos\MShop\Order\Item\Service\Iface Stripe service item 467 | */ 468 | protected function service( \Aimeos\MShop\Order\Item\Iface $basket ) : \Aimeos\MShop\Order\Item\Service\Iface 469 | { 470 | foreach( $basket->getService( 'payment' ) as $service ) 471 | { 472 | if( $service->getCode() === $this->getServiceItem()->getCode() ) { 473 | return $service; 474 | } 475 | } 476 | 477 | $this->throw( 'No Stripe service in basket', 'mshop' ); 478 | } 479 | } 480 | --------------------------------------------------------------------------------