Skip to content

Commit 6942d4e

Browse files
committed
feat(engine,sql): add BattlePay in-game shop with purchase flow and Shop Points currency
1 parent 49b88a9 commit 6942d4e

29 files changed

Lines changed: 2606 additions & 15 deletions
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-- See ARGUSCORE_FIXES.md for details.
2+
3+
-- BattlePay Phase 2: account-wide Shop Points balance, lazily created (INSERT ... ON DUPLICATE KEY
4+
-- UPDATE) on first credit rather than pre-populated for every account.
5+
--
6+
-- No in-server crediting queue/poller exists for this table on purpose - any external site with its
7+
-- own database access should write to this table directly, e.g.:
8+
-- INSERT INTO battlepay_account_balance (BattlenetAccountId, Balance) VALUES (?, ?)
9+
-- ON DUPLICATE KEY UPDATE Balance = Balance + VALUES(Balance);
10+
-- (safe to call repeatedly - no separate "does a row exist yet" check needed). Still deliberately
11+
-- NOT a webhook/HTTP endpoint on this server (security preference: no new network-facing surface) -
12+
-- the external site's own code/database access is the only integration point.
13+
CREATE TABLE IF NOT EXISTS `battlepay_account_balance` (
14+
`BattlenetAccountId` INT UNSIGNED NOT NULL,
15+
`Balance` BIGINT UNSIGNED NOT NULL DEFAULT 0,
16+
`UpdatedAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
17+
PRIMARY KEY (`BattlenetAccountId`),
18+
CONSTRAINT `fk_bpay_balance_bnet` FOREIGN KEY (`BattlenetAccountId`) REFERENCES `battlenet_accounts` (`id`) ON DELETE CASCADE
19+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='BattlePay: account-wide Shop Points balance';
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- See ARGUSCORE_FIXES.md for details.
2+
3+
-- BattlePay Phase 2: per-purchase audit trail. Realm-local (characters DB, not world/auth) because
4+
-- delivery (bags/mailbox) is realm-local, even though the Shop Points balance itself is account-wide
5+
-- (auth DB). DeliveryState is the crash-recovery ledger for the deliver-then-deduct purchase flow:
6+
-- 0 = delivered, balance not yet deducted (should always resolve to 1 within the same transaction
7+
-- chain - a row stuck at 0 indicates the server died mid-purchase; nothing to reconcile, since
8+
-- delivery and this row are written in the same characters-DB transaction)
9+
-- 1 = delivered and paid for (the normal end state)
10+
-- 2 = delivered but the balance deduction failed (a race with a concurrent external credit-queue
11+
-- change reduced the balance between the initial check and the deduct) - the server "ate" the
12+
-- cost rather than risk taking currency without delivering; flagged for admin reconciliation.
13+
CREATE TABLE IF NOT EXISTS `battlepay_purchase_log` (
14+
`Id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
15+
`BattlenetAccountId` INT UNSIGNED NOT NULL,
16+
`AccountId` INT UNSIGNED NOT NULL COMMENT 'game account (WorldSession::GetAccountId)',
17+
`CharacterGuid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'ObjectGuid::LowType is uint64 - 0 if delivered by mail to an offline TargetCharacter whose guid was only known from the client packet',
18+
`ProductID` INT UNSIGNED NOT NULL,
19+
`PurchaseID` BIGINT UNSIGNED NOT NULL COMMENT 'BattlePayMgr-generated id, unique - used to update DeliveryState later without needing the AUTO_INCREMENT Id back across an async transaction boundary',
20+
`PricePaid` BIGINT UNSIGNED NOT NULL COMMENT 'Shop Points, denormalized at purchase time so later price changes do not rewrite history',
21+
`DeliveryState` TINYINT UNSIGNED NOT NULL DEFAULT 0,
22+
`CreatedAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
23+
PRIMARY KEY (`Id`),
24+
UNIQUE KEY `uk_purchase_id` (`PurchaseID`),
25+
KEY `idx_character` (`CharacterGuid`),
26+
KEY `idx_bnet_account` (`BattlenetAccountId`),
27+
KEY `idx_delivery_state` (`DeliveryState`)
28+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='BattlePay: per-purchase audit trail';
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
-- See ARGUSCORE_FIXES.md for details.
2+
3+
CREATE TABLE IF NOT EXISTS `battlepay_display_info` (
4+
`DisplayInfoId` INT UNSIGNED NOT NULL,
5+
`CreatureDisplayInfoID` INT UNSIGNED NOT NULL DEFAULT 0,
6+
`FileDataID` INT UNSIGNED NOT NULL DEFAULT 0,
7+
`Flags` INT UNSIGNED NOT NULL DEFAULT 0,
8+
`Name1` VARCHAR(128) NOT NULL DEFAULT '',
9+
`Name2` VARCHAR(128) NOT NULL DEFAULT '',
10+
`Name3` VARCHAR(128) NOT NULL DEFAULT '',
11+
`Name4` VARCHAR(128) NOT NULL DEFAULT '',
12+
PRIMARY KEY (`DisplayInfoId`)
13+
);
14+
15+
CREATE TABLE IF NOT EXISTS `battlepay_display_info_locales` (
16+
`Id` INT UNSIGNED NOT NULL,
17+
`Locale` VARCHAR(4) NOT NULL,
18+
`Name1` VARCHAR(128) NULL,
19+
`Name2` VARCHAR(128) NULL,
20+
`Name3` VARCHAR(128) NULL,
21+
`Name4` VARCHAR(128) NULL,
22+
PRIMARY KEY (`Id`, `Locale`)
23+
);
24+
25+
CREATE TABLE IF NOT EXISTS `battlepay_display_info_visuals` (
26+
`DisplayInfoId` INT UNSIGNED NOT NULL,
27+
`DisplayId` INT UNSIGNED NOT NULL DEFAULT 0,
28+
`VisualId` INT UNSIGNED NOT NULL DEFAULT 0,
29+
`ProductName` VARCHAR(128) NOT NULL DEFAULT '',
30+
PRIMARY KEY (`DisplayInfoId`, `DisplayId`)
31+
);
32+
33+
CREATE TABLE IF NOT EXISTS `battlepay_product` (
34+
`ProductID` INT UNSIGNED NOT NULL,
35+
`Type` TINYINT UNSIGNED NOT NULL DEFAULT 0,
36+
`ChoiceType` TINYINT UNSIGNED NOT NULL DEFAULT 0,
37+
`Flags` INT UNSIGNED NOT NULL DEFAULT 0,
38+
`DisplayInfoID` INT UNSIGNED NOT NULL DEFAULT 0,
39+
`ScriptName` VARCHAR(64) NOT NULL DEFAULT '',
40+
`ClassMask` INT UNSIGNED NOT NULL DEFAULT 0,
41+
`WebsiteType` TINYINT UNSIGNED NOT NULL DEFAULT 0,
42+
-- Fix vs. reference: reference's CustomValue is declared but never populated from any DB
43+
-- column, silently breaking Gold/Level delivery. Here it's a real, loaded column.
44+
`CustomValue` INT UNSIGNED NOT NULL DEFAULT 0,
45+
-- Deviation from reference: three independent prices (0 = not offered via that currency)
46+
-- replace the reference's single NormalPriceFixedPoint/CurrentPriceFixedPoint, so a player can
47+
-- pay with Gold, Vote Points, or Donate Points.
48+
`GoldPrice` BIGINT UNSIGNED NOT NULL DEFAULT 0,
49+
`VotePointsPrice` INT UNSIGNED NOT NULL DEFAULT 0,
50+
`DonatePointsPrice` INT UNSIGNED NOT NULL DEFAULT 0,
51+
PRIMARY KEY (`ProductID`)
52+
);
53+
54+
CREATE TABLE IF NOT EXISTS `battlepay_product_item` (
55+
`ID` INT UNSIGNED NOT NULL AUTO_INCREMENT,
56+
`ProductID` INT UNSIGNED NOT NULL,
57+
`ItemID` INT UNSIGNED NOT NULL DEFAULT 0,
58+
`Quantity` INT UNSIGNED NOT NULL DEFAULT 1,
59+
`DisplayID` INT UNSIGNED NOT NULL DEFAULT 0,
60+
`PetResult` INT UNSIGNED NOT NULL DEFAULT 0,
61+
PRIMARY KEY (`ID`),
62+
INDEX `idx_product` (`ProductID`)
63+
);
64+
65+
CREATE TABLE IF NOT EXISTS `battlepay_product_group` (
66+
-- NOTE: GroupID = 22 is CLIENT-HARDCODED as the Services/character-boost tab. Never assign it
67+
-- to other content.
68+
`GroupID` INT UNSIGNED NOT NULL,
69+
`Name` VARCHAR(64) NOT NULL DEFAULT '',
70+
`IconFileDataID` INT UNSIGNED NOT NULL DEFAULT 0,
71+
`DisplayType` TINYINT UNSIGNED NOT NULL DEFAULT 0,
72+
`Ordering` SMALLINT NOT NULL DEFAULT 0,
73+
PRIMARY KEY (`GroupID`)
74+
);
75+
76+
CREATE TABLE IF NOT EXISTS `battlepay_product_group_locales` (
77+
`Id` INT UNSIGNED NOT NULL,
78+
`Locale` VARCHAR(4) NOT NULL,
79+
`Name` VARCHAR(64) NULL,
80+
PRIMARY KEY (`Id`, `Locale`)
81+
);
82+
83+
CREATE TABLE IF NOT EXISTS `battlepay_shop_entry` (
84+
`EntryID` INT UNSIGNED NOT NULL AUTO_INCREMENT,
85+
`GroupID` INT UNSIGNED NOT NULL,
86+
`ProductID` INT UNSIGNED NOT NULL,
87+
`Ordering` SMALLINT NOT NULL DEFAULT 0,
88+
`Flags` INT UNSIGNED NOT NULL DEFAULT 0,
89+
`BannerType` TINYINT UNSIGNED NOT NULL DEFAULT 0,
90+
`DisplayInfoID` INT UNSIGNED NOT NULL DEFAULT 0,
91+
PRIMARY KEY (`EntryID`),
92+
INDEX `idx_group` (`GroupID`),
93+
INDEX `idx_product` (`ProductID`)
94+
);
95+
96+
-- Seed data: a couple of real, verified Legion items (confirmed present in this build's
97+
-- ItemSparse.csv export) for Phase 1 verification (product list display only, not purchasable
98+
-- yet - the purchase flow is added in a later phase).
99+
100+
DELETE FROM `battlepay_product_group` WHERE `GroupID` IN (1, 10);
101+
INSERT INTO `battlepay_product_group` (`GroupID`, `Name`, `IconFileDataID`, `DisplayType`, `Ordering`) VALUES
102+
(1, 'Mounts', 132261, 0, 0),
103+
(10, 'Toys', 237429, 0, 1);
104+
105+
DELETE FROM `battlepay_display_info` WHERE `DisplayInfoId` IN (1, 2, 3);
106+
INSERT INTO `battlepay_display_info` (`DisplayInfoId`, `Name1`, `Name3`) VALUES
107+
(1, 'Jeweled Onyx Panther', 'A shimmering onyx panther mount.'),
108+
(2, 'Reins of the Grove Warden', 'A loyal warden of the grove, ready to carry you into battle.'),
109+
(3, 'Snowball', 'Throw it at a friend.');
110+
111+
DELETE FROM `battlepay_product` WHERE `ProductID` IN (1, 2, 3);
112+
INSERT INTO `battlepay_product` (`ProductID`, `Type`, `WebsiteType`, `DisplayInfoID`, `GoldPrice`, `VotePointsPrice`, `DonatePointsPrice`) VALUES
113+
(1, 0, 21, 1, 0, 50, 0), -- Jeweled Onyx Panther - Vote Points only
114+
(2, 0, 21, 2, 500000, 0, 250), -- Reins of the Grove Warden - Gold or Donate Points
115+
(3, 0, 3, 3, 1000, 5, 0); -- Snowball - Gold or Vote Points
116+
117+
DELETE FROM `battlepay_product_item` WHERE `ProductID` IN (1, 2, 3);
118+
INSERT INTO `battlepay_product_item` (`ProductID`, `ItemID`, `Quantity`) VALUES
119+
(1, 82453, 1),
120+
(2, 128422, 1),
121+
(3, 17202, 5);
122+
123+
DELETE FROM `battlepay_shop_entry` WHERE `ProductID` IN (1, 2, 3);
124+
INSERT INTO `battlepay_shop_entry` (`GroupID`, `ProductID`, `Ordering`) VALUES
125+
(1, 1, 0),
126+
(1, 2, 1),
127+
(10, 3, 0);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-- See ARGUSCORE_FIXES.md for details.
2+
3+
-- Phase 2: collapse the 3-currency pricing model (GoldPrice/VotePointsPrice/DonatePointsPrice) into
4+
-- a single account-wide Shop Points price. The real 7.3.5 client purchase flow
5+
-- (C_StoreSecure.PurchaseProduct/PurchaseProductConfirm) has no currency-selector field at all, so
6+
-- per-purchase currency choice was never actually possible through the native Store UI.
7+
8+
-- Plain ADD/DROP COLUMN (no IF [NOT] EXISTS) - that clause isn't reliably supported across the
9+
-- MySQL/MariaDB versions this might run against. The DB updater tool tracks applied migrations
10+
-- itself and won't re-run this file under normal use, so idempotency here isn't required the way
11+
-- it is for files sometimes applied directly via a raw mysql client.
12+
ALTER TABLE `battlepay_product`
13+
ADD COLUMN `ShopPointsPrice` BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER `CustomValue`;
14+
15+
UPDATE `battlepay_product` SET `ShopPointsPrice` = 500 WHERE `ProductID` = 1; -- Jeweled Onyx Panther
16+
UPDATE `battlepay_product` SET `ShopPointsPrice` = 1000 WHERE `ProductID` = 2; -- Reins of the Grove Warden
17+
UPDATE `battlepay_product` SET `ShopPointsPrice` = 50 WHERE `ProductID` = 3; -- Snowball
18+
19+
ALTER TABLE `battlepay_product`
20+
DROP COLUMN `GoldPrice`,
21+
DROP COLUMN `VotePointsPrice`,
22+
DROP COLUMN `DonatePointsPrice`;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
-- See ARGUSCORE_FIXES.md for details.
2+
3+
-- BattlePay: real icons + 3D "View in 3D" preview for the 3 seeded shop products. The original
4+
-- Phase 1 seed left `battlepay_display_info.FileDataID` at its default (0), so the client fell back
5+
-- to the placeholder "?" icon for every tile, and left `battlepay_display_info_visuals` empty, so no
6+
-- product ever had a 3D model card to show.
7+
--
8+
-- FileDataID values are each item's own icon, read directly from Item.db2 (`IconFileDataID` column,
9+
-- keyed by ItemID - logs/db2csv/Item.csv), not guessed. DisplayId/VisualId values for the two mounts
10+
-- are each mount's CreatureDisplayInfoID (from MountXDisplay.csv, keyed by MountID) and UiModelSceneID
11+
-- (from Mount.csv, keyed by the item's on-use SpellID from ItemEffect.csv) - the same chain the real
12+
-- client walks (Item -> on-use Spell -> Mount -> MountXDisplay) to know what to render when the mount
13+
-- is actually summoned. Snowball (a thrown consumable, not a mount) intentionally gets an icon only,
14+
-- no Visuals row - matches how the real Shop displays non-mount items.
15+
16+
UPDATE `battlepay_display_info` SET `FileDataID` = 603364 WHERE `DisplayInfoId` = 1; -- Jeweled Onyx Panther (ItemID 82453)
17+
UPDATE `battlepay_display_info` SET `FileDataID` = 1129627 WHERE `DisplayInfoId` = 2; -- Reins of the Grove Warden (ItemID 128422)
18+
UPDATE `battlepay_display_info` SET `FileDataID` = 132387 WHERE `DisplayInfoId` = 3; -- Snowball (ItemID 17202)
19+
20+
DELETE FROM `battlepay_display_info_visuals` WHERE `DisplayInfoId` IN (1, 2);
21+
INSERT INTO `battlepay_display_info_visuals` (`DisplayInfoId`, `DisplayId`, `VisualId`, `ProductName`) VALUES
22+
(1, 42185, 4, 'Jeweled Onyx Panther'), -- Mount ID 451, CreatureDisplayInfoID 42185, UiModelSceneID 4
23+
(2, 64583, 4, 'Reins of the Grove Warden'); -- Mount ID 764, CreatureDisplayInfoID 64583, UiModelSceneID 4
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- See ARGUSCORE_FIXES.md for details.
2+
3+
-- BattlePay: the original Phase 1 seed for `battlepay_shop_entry` never set `DisplayInfoID` (column
4+
-- exists, just wasn't in the INSERT's column list), so it stayed at its default 0 for all 3 rows.
5+
-- `BattlePayMgr::SendProductList()` builds the "Shop" array (WorldPackets::BattlePay::
6+
-- BattlePayShopEntry) from this table, and calls WriteDisplayInfo(entry.DisplayInfoID, ...)
7+
-- independently of the "ProductInfo"/"Product" arrays (which use battlepay_product.DisplayInfoID,
8+
-- already correct) - with DisplayInfoID=0, WriteDisplayInfo always returns "no display info" for the
9+
-- Shop array specifically, regardless of the icon/model fix in 2026_08_14_00_world.sql. If the
10+
-- client's product-tile grid renders from this Shop array (suspected but not proven), its per-entry
11+
-- DisplayInfo would always have been empty. Filling it in with the same DisplayInfoId already used by
12+
-- each row's product, to close this gap alongside the earlier icon/model fix.
13+
14+
UPDATE `battlepay_shop_entry` SET `DisplayInfoID` = 1 WHERE `ProductID` = 1; -- Jeweled Onyx Panther
15+
UPDATE `battlepay_shop_entry` SET `DisplayInfoID` = 2 WHERE `ProductID` = 2; -- Reins of the Grove Warden
16+
UPDATE `battlepay_shop_entry` SET `DisplayInfoID` = 3 WHERE `ProductID` = 3; -- Snowball
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- See ARGUSCORE_FIXES.md for details.
2+
3+
-- Emergency revert of 2026_08_14_01_world.sql: populating battlepay_shop_entry.DisplayInfoID caused
4+
-- the real client to fail with "Not enough memory" (Battle.net support article 6926) - a much worse
5+
-- regression than the icon/3D-model issue it was meant to fix. Reverting immediately; see
6+
-- ARGUSCORE_FIXES.md "Follow-up 5" for the investigation.
7+
8+
UPDATE `battlepay_shop_entry` SET `DisplayInfoID` = 0 WHERE `ProductID` IN (1, 2, 3);

src/server/database/Database/Implementation/CharacterDatabase.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
723723
PrepareStatement(CHAR_UPD_CHARACTER_INSTANCE_LOCK_FORCE_EXPIRE, "UPDATE character_instance_lock SET expiryTime = ?, extended = 0 WHERE guid = ? AND mapId = ? AND lockId = ?", CONNECTION_ASYNC);
724724
PrepareStatement(CHAR_DEL_INSTANCE, "DELETE FROM instance WHERE instanceId = ?", CONNECTION_ASYNC);
725725
PrepareStatement(CHAR_INS_INSTANCE, "INSERT INTO instance (instanceId, data, completedEncountersMask, entranceWorldSafeLocId) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
726+
727+
// BattlePay Shop Points - see ARGUSCORE_FIXES.md
728+
PrepareStatement(CHAR_INS_BATTLEPAY_PURCHASE_LOG, "INSERT INTO battlepay_purchase_log (BattlenetAccountId, AccountId, CharacterGuid, ProductID, PurchaseID, PricePaid, DeliveryState) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
729+
PrepareStatement(CHAR_UPD_BATTLEPAY_PURCHASE_LOG_STATE, "UPDATE battlepay_purchase_log SET DeliveryState = ? WHERE PurchaseID = ?", CONNECTION_ASYNC);
726730
}
727731

728732
CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo, ConnectionFlags connectionFlags) : MySQLConnection(connInfo, connectionFlags)

src/server/database/Database/Implementation/CharacterDatabase.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,10 @@ enum CharacterDatabaseStatements : uint32
606606
CHAR_DEL_INSTANCE,
607607
CHAR_INS_INSTANCE,
608608

609+
// BattlePay Shop Points - see ARGUSCORE_FIXES.md
610+
CHAR_INS_BATTLEPAY_PURCHASE_LOG,
611+
CHAR_UPD_BATTLEPAY_PURCHASE_LOG_STATE,
612+
609613
MAX_CHARACTERDATABASE_STATEMENTS
610614
};
611615

src/server/database/Database/Implementation/LoginDatabase.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,20 @@ void LoginDatabaseConnection::DoPrepareStatements()
188188
PrepareStatement(LOGIN_SEL_BNET_ITEM_FAVORITE_APPEARANCES, "SELECT itemModifiedAppearanceId FROM battlenet_item_favorite_appearances WHERE battlenetAccountId = ?", CONNECTION_ASYNC);
189189
PrepareStatement(LOGIN_INS_BNET_ITEM_FAVORITE_APPEARANCE, "INSERT INTO battlenet_item_favorite_appearances (battlenetAccountId, itemModifiedAppearanceId) VALUES (?, ?)", CONNECTION_ASYNC);
190190
PrepareStatement(LOGIN_DEL_BNET_ITEM_FAVORITE_APPEARANCE, "DELETE FROM battlenet_item_favorite_appearances WHERE battlenetAccountId = ? AND itemModifiedAppearanceId = ?", CONNECTION_ASYNC);
191+
192+
// BattlePay Shop Points - see ARGUSCORE_FIXES.md
193+
PrepareStatement(LOGIN_SEL_BATTLEPAY_BALANCE, "SELECT Balance FROM battlepay_account_balance WHERE BattlenetAccountId = ?", CONNECTION_ASYNC);
194+
// No "AND Balance >= ?" guard needed - Balance is BIGINT UNSIGNED and the server runs with
195+
// sql_mode=STRICT_TRANS_TABLES, so an UPDATE that would underflow it errors out (caught via the
196+
// standard AsyncCommitTransaction().AfterComplete(bool success) pattern) rather than wrapping or
197+
// going negative.
198+
PrepareStatement(LOGIN_UPD_BATTLEPAY_BALANCE_DEDUCT, "UPDATE battlepay_account_balance SET Balance = Balance - ? WHERE BattlenetAccountId = ?", CONNECTION_ASYNC);
199+
// Also the exact statement an external site's own code can mirror to credit a balance directly
200+
// (INSERT ... ON DUPLICATE KEY UPDATE Balance = Balance + ? - safe to call repeatedly, no
201+
// separate "does a row exist yet" check needed).
202+
PrepareStatement(LOGIN_INS_BATTLEPAY_BALANCE_CREDIT, "INSERT INTO battlepay_account_balance (BattlenetAccountId, Balance) VALUES (?, ?) "
203+
"ON DUPLICATE KEY UPDATE Balance = Balance + VALUES(Balance)", CONNECTION_BOTH);
204+
PrepareStatement(LOGIN_SEL_BATTLENET_ACCOUNT_BY_ACCOUNT_ID, "SELECT battlenet_account FROM account WHERE id = ?", CONNECTION_SYNCH);
191205
}
192206

193207
LoginDatabaseConnection::LoginDatabaseConnection(MySQLConnectionInfo& connInfo, ConnectionFlags connectionFlags) : MySQLConnection(connInfo, connectionFlags)

0 commit comments

Comments
 (0)